1. 程式人生 > >Java多執行緒(ExecutorService 、Executors、Callable、Future、FutureTask)

Java多執行緒(ExecutorService 、Executors、Callable、Future、FutureTask)

前言:我們一般通過繼承Thread類重寫run方法或者實現runnable介面重寫run方法,最後建立和啟動一個執行緒,但是都需要自己建立、啟動Thread物件。執行緒池可以實現幫助我們管理Thread物件,至於要使用幾個執行緒,什麼時候啟動這些執行緒,是開啟多個執行緒還是用單個執行緒來完成這些任務,我們無需操心。

Java通過Executors提供四種執行緒池

  1.  newCachedThreadPool:建立一個可快取執行緒池,如果執行緒池長度超過處理需要,可靈活回收空閒執行緒,若無可回收,則新建執行緒。(執行緒最大併發數不可控制)
  2. newFixedThreadPool:建立一個定長執行緒池,可控制執行緒最大併發數,超出的執行緒會在佇列中等待。
  3. newScheduledThreadPool:建立一個定長執行緒池,支援定時及週期性任務執行。
  4. newSingleThreadExecutor:建立一個單執行緒化的執行緒池,它只會用唯一的工作執行緒來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先順序)執行。

 

package threadTest.executors;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @ClassName CachedThreadPool
 * @Description TODO
 * @Author Kikityer
 * @Date 2018/11/21 19:04
 * @Version 1.0.0
 **/
public class CachedThreadPool {
    public static void main(String[] args) {
        ExecutorService exec = Executors.newCachedThreadPool();
        for (int i = 0;i < 5; i++){
            exec.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName()+" is doing task");
                }
            });
        }
        exec.shutdown();

    }
}


//結果:
/**
 *pool-1-thread-1 is doing task
 *pool-1-thread-2 is doing task
 *pool-1-thread-3 is doing task
 *pool-1-thread-4 is doing task
 *pool-1-thread-5 is doing task
 */

通過for迴圈建立了5個任務,通過submit方法開啟了五個執行緒處理這五個任務。

submit有兩個常用的方法:

Future<?> submit(Runnable task)           接收Runnable型別入參,而Runnable無返回值 。

<T> Future<T> submit(Callable<T> task)        接收Callable型別入參 ,Callable入參允許任務返回值。

 

package threadTest.executors;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

/**
 * @ClassName CallableAndFuture
 * @Description TODO
 * @Author Kikityer
 * @Date 2018/11/21 19:20
 * @Version 1.0.0
 **/
public class CallableAndFuture {
    public static void main(String[] args) {
        ExecutorService exec = Executors.newCachedThreadPool();
        List<Callable<String>> taskList = new ArrayList<>(); //任務列表,用於存放任務
        for (int i = 0; i < 5; i++){
            taskList.add(new MyCallable()); //建立5個任務,並且新增到列表中
        }

        List<Future<String>> resultList = new ArrayList<>(); //結果列表,用於存放任務執行的結果


        try {
            resultList = exec.invokeAll(taskList);  //invokeAll是等所有任務完成後返回代表結果的Future列表
            exec.shutdown();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        for (Future<String> future : resultList){  //遍歷結果列表,打印出每個任務的結果,即打印出每個當前執行緒的名字(String型別)
            try {
                System.out.println(future.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }

    }
}


/**
 * MyCallable實現Callable介面,建立一個可以有返回值的任務類
 */
class MyCallable implements Callable<String>{
    @Override
    public String call() {
        return Thread.currentThread().getName();
    }
}


//結果:
/**
 *pool-1-thread-1
 *pool-1-thread-2
 *pool-1-thread-3
 *pool-1-thread-4
 *pool-1-thread-5
 */

在上面的程式碼中,我們建立了一個類實現了一個Callable介面,此類是一個有返回值的任務類。而後開啟了一個執行緒池用來管理執行緒。從結果可以看出我們得到了任務類所返回的資訊。其中用到了Future介面、invokeAll方法、get方法。

Future介面

Future 表示非同步計算的結果。它提供了檢查計算是否完成的方法,以等待計算的完成,並獲取計算的結果。上面的例子中exec執行器執行了一個Callable型別的任務列表然後得到了Futuer型別的結果列表resultList。

invokeAll方法

invokeAll批量執行所有任務。和submit的區別是:submit提交單個任務

get方法

等待計算完成,獲取其結果。get方法會阻塞直到結果返回。

FutureTask

FutureTask類是 Future 介面的一個實現。FutureTask類實現了RunnableFuture介面,RunnableFuture繼承了Runnable介面和Future介面所以:

可以看出FutureTask可以當作一個有返回值的Runnable任務來用。 

  • FutureTask可以作為Runnable被執行緒執行
  • 可以作為Future得到傳入的Callable物件的返回值     如:futureTask.get()