【Java多線程】多線程實現的四種方式


多線程實現的方式有很多,本文只針對常見的四種方式。

一、繼承Thread類,重寫run方法

該方式無返回值,方式如下:

public class MyThread extends Thread{
    private String param;
    public MyThread(String param){
        this.param = param;
    }
    @Override
    public void run(){
        System.out.println("Current Thread Param:" + param
                +",Current Thread Name:"+Thread.currentThread().getName());
    }
    public static void main(String[] args){
        MyThread myThread = new MyThread("My Thread");
        myThread.start();
        System.out.println("Current Thread Name:"+Thread.currentThread().getName());
    }
}

輸出結果:

Current Thread Param:My Thread,Current Thread Name:Thread-0
Current Thread Name:main

二、實現Runnable接口,重寫run方法

該方式無返回值,方式如下:

public class MyRunnable implements Runnable{
    private String param;
    public MyRunnable(String param){
        this.param = param;
    }
    @Override
    public void run() {
        System.out.println("Current Thread Param:" + param
                +",Current Thread Name:"+Thread.currentThread().getName());
    }

    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable("My Runnable");
        Thread thread = new Thread(myRunnable);
        thread.start();
        System.out.println("Current Thread Name:"+Thread.currentThread().getName());
    }
}

輸出結果:

Current Thread Param:My Runnable,Current Thread Name:Thread-0
Current Thread Name:main

三、通過Callable和FutureTask創建線程

Callable是類似於Runnable的接口,實現Callable接口的類和實現Runnable的類都是可被其它線程執行的任務,只是 Callable 的 call() 方法允許線程有返回值,並且可拋出異常,而Runnable 的 run()方法是不能拋出異常的。

該方式有返回值,可以獲取線程返回值,方式如下:

public class MyCallable implements Callable<String> {
    private String param;

    public MyCallable(String param){
        this.param = param;
    }

    @Override
    public String call() throws Exception {
        System.out.println("Current Thread Param:" + param
                +",Current Thread Name:"+Thread.currentThread().getName());
        Thread.sleep(2000);
        return param + " is executed";
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        MyCallable myCallable = new MyCallable("My Callable");
        FutureTask<String> futureTask = new FutureTask(myCallable);
        Thread thread = new Thread(futureTask);
        thread.start();
        System.out.println("Current Thread Name:"+Thread.currentThread().getName());
        if (!futureTask.isDone()){
            System.out.println("thread task is not finished, plase wait");
        }
        System.out.println("Thread Return:"+futureTask.get());

    }
}

輸出結果:

Current Thread Param:My Callable,Current Thread Name:Thread-0
Current Thread Name:main
thread task is not finished, plase wait
Thread Return:My Callable is executed

總結:
首先,FutureTask類的實現繼承關係,結構圖如下:
FutureTask繼承關係圖

  1. FutureTask 實現了 RunnableFuture 接口
  2. RunnableFuture 接口多繼承了 Runnable 和 Future 接口類
  3. Runnable 接口只有一個 run 方法
    @FunctionalInterface
    public interface Runnable {
    public abstract void run();
    }
    
  4. Future 就是對於具體的Runnable或者Callable任務的執行結果進行取消、查詢是否完成、獲取結果、設置結果操作,接口中有以下幾個方法:
    public interface Future<V> {
    	//用來取消異步任務的執行。如果異步任務已經完成或者已經被取消,或者由於某些原因不能取消,則會返回false。
    	//如果任務還沒有被執行,則會返回true並且異步任務不會被執行。
    	boolean cancel(boolean mayInterruptIfRunning);
    	//判斷任務是否被取消,如果任務在結束(正常執行結束或者執行異常結束)前被取消則返回true,否則返回false。
    	boolean isCancelled();
    	//判斷任務是否已經完成,如果完成則返回true,否則返回false。
    	//需要注意的是:任務執行過程中發生異常、任務被取消也屬於任務已完成,也會返回true。
    	boolean isDone();
    	//獲取任務執行結果,如果任務還沒完成則會阻塞等待直到任務執行完成。如果任務被取消則會拋出CancellationException異常,
    	//如果任務執行過程發生異常則會拋出ExecutionException異常,如果阻塞等待過程中被中斷則會拋出InterruptedException異常。
    	V get() throws InterruptedException, ExecutionException;
    	//帶超時時間的get()版本,如果阻塞等待過程中超時則會拋出TimeoutException異常。
    	V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
    }
    

四、通過線程池創建線程

仍然利用上一個方法中定義好的 MyCallable 類。
該方式有返回值,可以獲取線程返回值,方式如下:

public class MyThreadPool {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService executorService = Executors.newCachedThreadPool();
        Future<String> f = executorService.submit(new MyCallable("My Callable 1"));
        if (!f.isDone()){
            System.out.println("thread task is not finished, plase wait");
        }
        System.out.println("Thread Return:"+f.get());
    }
}

輸出結果:

thread task is not finished, plase wait
Current Thread Param:My Callable 1,Current Thread Name:pool-1-thread-1
Thread Return:My Callable 1 is executed

總結:
首先了解一下Executor的繼承關係圖:
Executor的繼承關係圖
ExecutorService、Callable 都是屬於 Executor。Executor框架是Java 5中引入的,其內部使用了線程池機制,它在 java.util.cocurrent 包下,通過該框架來控制線程的啓動、執行和關閉,可以簡化併發編程的操作。

  • Executor:一個接口,其定義了一個接收 Runnable 對象的方法 execute。一般來說,Runnable 任務開闢在新線程中的使用方法爲:new Thread(new RunnableTask())).start(),但在 Executor 中,可以使用 Executor 而不用顯示地創建線程:executor.execute(new RunnableTask());
  • ExecutorService:是 Executor 的子類接口,其提供了生命週期管理的方法,返回 Future 對象,以及可跟蹤一個或多個異步任務執行狀況返回Future的方法;可以調用 ExecutorService 的 shutdown() 方法來平滑地關閉 ExecutorService,調用該方法後,將導致ExecutorService停止接受任何新的任務且等待已經提交的任務執行完成(已經提交的任務會分兩類:一類是已經在執行的,另一類是還沒有開始執行的),當所有已經提交的任務執行完畢後將會關閉ExecutorService。因此一般用該接口來實現和管理多線程。
  • Executors:提供了一系列工廠方法用於創建線程池,返回的線程池都實現了ExecutorService接口(返回的線程池繼承了 AbstractExecutorService 抽象類,而該抽象類實現了 ExecutorService 接口)。
    • newFixedThreadPool:創建固定數目線程的線程池
    • newCachedThreadPool:創建一個可緩存的線程池,調用execute 將重用以前構造的線程(如果線程可用)。如果現有線程沒有可用的,則創建一個新線程並添加到池中。終止並從緩存中移除那些已有 60 秒鐘未被使用的線程。
    • newScheduledThreadPool:創建一個支持定時及週期性的任務執行的線程池
    • newSingleThreadExecutor:創建一個單線程化的Executor

Executor VS ExecutorService VS Executors

  1. Executor 和 ExecutorService 這兩個接口主要的區別是:ExecutorService 接口繼承了 Executor 接口,是 Executor 的子接口。
  2. Executor 和 ExecutorService 第二個區別是:Executor 接口定義了 execute()方法用來接收一個 Runnable 接口的對象,而 ExecutorService 接口中的 submit()方法可以接受 Runnable 和 Callable 接口的對象。
  3. Executor 和 ExecutorService 接口第三個區別是 Executor 中的 execute() 方法不返回任何結果,而 ExecutorService 中的 submit()方法返回一個 Future 對象,可以通過這個 Future 對象返回線程結果。
  4. Executor 和 ExecutorService 接口第四個區別是 ExecutorService 還提供用來控制線程池的方法。比如:調用 shutDown() 方法終止線程池。
  5. Executors 類提供工廠方法用來創建不同類型的線程池。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章