什麼是線程池?如何創建一個Java線程池?

一個線程池管理了一組工作線程,同時它還包括了一個用於放置等待執行的任務的隊列。線程池可以避免線程的頻繁創建與銷燬,降低資源的消耗,提高系統的反應速度。java.util.concurrent.Executors提供了幾個java.util.concurrent.Executor接口的實現用於創建線程池,其主要涉及四個角色:

  • 線程池:Executor
  • 工作線程:Worker線程,Worker的run()方法執行Job的run()方法
  • 任務Job:Runable和Callable
  • 阻塞隊列:BlockingQueue 

 

 

一、 線程池Executor

  Executor及其實現類是用戶級的線程調度器,也是對任務執行機制的抽象,其將任務的提交與任務的執行分離開來,核心實現類包括ThreadPoolExecutor(用來執行被提交的任務)和ScheduledThreadPoolExecutor(可以在給定的延遲後執行任務或者週期性執行任務)。

Executor的實現繼承鏈條爲:(父接口)Executor -> (子接口)ExecutorService -> (實現類)[ ThreadPoolExecutor + ScheduledThreadPoolExecutor ]。

 

二、任務Runable/Callable

  Runnable(run)和Callable(call)都是對任務的抽象,但是Callable可以返回任務執行的結果或者拋出異常。

 

三、任務執行狀態Future

  Future是對任務執行狀態和結果的抽象,核心實現類是furtureTask (所以它既可以作爲Runnable被線程執行,又可以作爲Future得到Callable的返回值) ;

 

   

(1). 使用Callable+Future獲取執行結果

 ExecutorService executor = Executors.newCachedThreadPool();
    Task task = new Task();
    Future<Integer> result = executor.submit(task);
    System.out.println("task運行結果" + result.get());    

    class Task implements Callable<Integer>{
            @Override
            public Integer call() throws Exception {
                System.out.println("子線程在進行計算");
                Thread.sleep(3000);
                int sum = 0;
                for(int i=0;i<100;i++)
                    sum += i;
                return sum;
            }

 

(2). 使用Callable + FutureTask獲取執行結果

  ExecutorService executor = Executors.newCachedThreadPool();
     Task task = new Task();
     FutureTask<Integer> futureTask = new FutureTask<Integer>(task);
     executor.submit(futureTask);  
    System.out.println("task運行結果"+futureTask.get());

    class Task implements Callable<Integer>{
            @Override
            public Integer call() throws Exception {
                System.out.println("子線程在進行計算");
                Thread.sleep(3000);
                int sum = 0;
                for(int i=0;i<100;i++)
                    sum += i;
                return sum;
            }

 

 

四、四種常用的線程池

 

(1). FixedThreadPool

  用於創建使用固定線程數的ThreadPool,corePoolSize = maxPoolSize = n(固定的含義),阻塞隊列爲LinkedBlockingQueue。

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

 

 

(2). SingleThreadExecutor

  用於創建一個單線程的線程池,corePoolSize = maxPoolSize = 1,阻塞隊列爲LinkedBlockingQueue。

 public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

 

(3). CachedThreadPool

  用於創建一個可緩存的線程池,corePoolSize = 0, maxPoolSize = Integer.MAX_VALUE,阻塞隊列爲SynchronousQueue(沒有容量的阻塞隊列,每個插入操作必須等待另一個線程對應的移除操作,反之亦然),是根據需求創建新線程的,需求多時,創建的就多,需求少時,JVM自己會慢慢的釋放掉多餘的線程。

  public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

 

(4). ScheduledThreadPoolExecutor

用於創建一個大小無限的線程池,此線程池支持定時以及週期性執行任務的需求。

public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.NANOSECONDS,
              new DelayedWorkQueue());
    }

  

 

五、線程池的飽和策略

 

當阻塞隊列滿了,且沒有空閒的工作線程,如果繼續提交任務,必須採取一種策略處理該任務,線程池提供了4種策略:

 

  • AbortPolicy:直接拋出異常,默認策略;
  • CallerRunsPolicy:用調用者所在的線程來執行任務;
  • DiscardOldestPolicy:丟棄阻塞隊列中最老的任務,並執行當前任務;
  • DiscardPolicy:直接丟棄任務;

 

  當然也可以根據應用場景實現RejectedExecutionHandler接口,自定義飽和策略,如記錄日誌或持久化存儲不能處理的任務。

 

 

六、 線程池調優

 

  • 設置最大線程數,防止線程資源耗盡;
  • 使用有界隊列,從而增加系統的穩定性和預警能力(飽和策略);
  • 根據任務的性質設置線程池大小:CPU密集型任務(CPU個數個線程),IO密集型任務(CPU個數兩倍的線程),混合型任務(拆分)。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章