異步多線程----執(zhí)行器(Executor)

??如果程序中創(chuàng)建了大量的生命期很短的線程,應該使用線程池(thread pool)。一個線程池中包含許多準備運行的空線程。將Runnable對象交給線程池,就會有一個線程調用run方法。當run方法退出時,線程不會死亡,而是在池中準備為下一個請求提供服務。
??另一個使用線程池的理由是減少并發(fā)線程的數(shù)目創(chuàng)建大量線程會大大降低性能甚至使虛擬機崩潰。如果有一個會創(chuàng)建許多線程的算法,應該使用一個線程數(shù)“固定的”線程池以限制并發(fā)線程的總數(shù)。

??執(zhí)行器(Executor)類有許多靜態(tài)工廠方法用來構建線程池。

newCachedThreadPool      |       必要時創(chuàng)建新線程;空閑線程會被保留60秒
newFixedThreadPool       |       該池包含固定數(shù)量的線程;空閑線程會一直被保留。
newSingleThreadExecutor  |       只有一個線程的“池”,該線程順序執(zhí)行每一個提交的任務
newScheduledThreadPool   |       用于預定執(zhí)行而構建的固定線程池,替代java.util.Timer
newSingleThreadScheduledExecutor 用于預定執(zhí)行而構建的單線程“池”

1.線程池

  • newCachedThreadPool方法構建了一個線程池,對于每個任務,如果有空線程可用,立即讓它執(zhí)行任務,如果沒有可用的空線程,則創(chuàng)建一個新線程。
  • newFixedThreadPool方法構建一個具有固定大小的線程池。如果提交的任務數(shù)多于空閑的線程數(shù),那么把得不到服務的任務放置到隊列中。
  • newSingleThreadExecutor是一個退化了的大小為1的線程池:由一個線程執(zhí)行提交的任務,一個接著一個。

??上面3個方法返回實現(xiàn)了ExecutorService接口ThreadPoolExecutor類的對象

??
??可以使用下面的方法將一個Runnable對象或Callable對象提交給ExecutorService

//提交指定的任務去執(zhí)行
Future<?> submit(Runnable task)
Future<T> submit(Runnable task, T result)
Future<T> submit(Callable<T> task)

??該線程池會在方便的時候盡早執(zhí)行提交的任務。調用submit時,會得到一個Future對象,可用來查詢該任務的狀態(tài)。
??第一個submit方法返回一個Future<?>??梢允褂眠@樣一個對象來調用isDone、cancel或isCancelled。但是,get方法在完成的時候只是簡單地返回null。
??第二版本的Submit也提交一個Runnable,并且Future的get方法在完成的時候返回指定的result對象。
??第三個版本的Submit提交一個Callable,并且返回的Future對象將在計算結構準備好的時候得到它。

??當用完一個線程池的時候,調用shutdown。該方法啟動該池的關閉序列。被關閉的執(zhí)行器不再接受新的任務。當所有任務都完成以后,線程池中的線程死亡。另一種方法是調用shutdownNow。該池取消尚未開始的所有任務并試圖中斷正在運行的線程

??總結在使用線程池時應該做的事:

1.調用Executors類中靜態(tài)的方法newCachedThreadPool或newFixedThreadPool。
2.調用submit提交Runnable或Callable對象。
3.如果想要取消一個任務,或如果提交Callable對象,那就要保存好返回的Future對象。
4.當不再提交任何任務時,調用shutdown。

??繼續(xù)以一個計算匹配的文件數(shù)目程序為例:

class MatchCounter implements Callable<Integer>
{
    private File directory;
    private String keyword;
    private ExecutorService pool;
    private int count;

    public MatchCounter(File directory, String keyword, ExecutorService pool) {
          this.directory = directory;
          this.keyword = keyword;
          this.pool = pool;
}

    public Integer call() {
          count = 0;
          try {
                File [] files = directory.listFiles();
                List<Future<Integer>> results = new ArrayList<>();

                for ( File file : files) {
                    if ( file.isDirectory()) {
                        MatchCounter counter = new MatchCounter(file, keyword, pool);
                        Future<Integer> result = pool.submit(counter);
                        results.add(result);
                    }
                    else {
                        if( search(file)) count++;
                    }
                }

                for (Future<Integer> result : results) {
                    try{
                            count += result.get();  
                        }
                    catch (ExecutionException e) {
                            e.printStackTrace();
                        }
                 }
          }
          catch (InterruptedException e) {}    

          return count;

      }

}

public boolean search(File file) {...}

public class FutureTest {
      public static void main(String[] args) {
          MatchCounter counter = new MatchCounter(new File(directory), keyword, pool);
          Future<Integer> result = pool.submit(counter);
          ...
          pool.shutdown();
      }
}

2.預定執(zhí)行

??ScheduledExecutorService接口具有為預定執(zhí)行(Scheduled Execution)或重復執(zhí)行任務而設計的方法。它是一種允許使用線程池機制的java.util.Timer的泛化。Executors類的newScheduledThreadPoolnewSingleThreadScheduledExecutor方法將返回實現(xiàn)了ScheduledExecutorService接口的對象。
??可以預定Runnable或Callable在初始的延遲之后只運行一次。也可以預定一個Runnable對象周期性地運行。詳見API。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內容