Java多線程)快三最快回本方法81512073

熟悉Java多線程編程的同窗都知道,當咱們線程建立過多時,容易引起內存溢出,所以咱們就有必要使用線程池的技術了。java

目錄數據庫

1 線程池的優點編程

2 線程池的使用數組

3 線程池的工做原理緩存

4 線程池的參數多線程

4.1 任務隊列(workQueue)併發

4.2 線程工廠(threadFactory)框架

4.3 拒絕策略(handler)dom

5 功能線程池ide

5.1 定長線程池(FixedThreadPool)

5.2 定時線程池(ScheduledThreadPool )

5.3 可緩存線程池(CachedThreadPool)

5.4 單線程化線程池(SingleThreadExecutor)

5.5 對比

6 總結

參考


1 線程池的優點

整體來講,線程池有以下的優點:

(1)下降資源消耗。經過重複利用已建立的線程下降線程建立和銷燬形成的消耗。

(2)提升響應速度。當任務到達時,任務能夠不須要等到線程建立就能當即執行。

(3)提升線程的可管理性。線程是稀缺資源,若是無限制的建立,不只會消耗系統資源,還會下降系統的穩定性,使用線程池能夠進行統一的分配,調優和監控。

2 線程池的使用

線程池的真正實現類是ThreadPoolExecutor,其構造方法有以下4種:

1.  public ThreadPoolExecutor(int corePoolSize,
    
2.   int maximumPoolSize,
    
3.   long keepAliveTime,
    
4.   TimeUnit unit,
    
5.   BlockingQueue<Runnable> workQueue) {
    
6.      this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
    
7.           Executors.defaultThreadFactory(), defaultHandler);
    
8.  }
    

10.  public ThreadPoolExecutor(int corePoolSize,
    
11.   int maximumPoolSize,
    
12.   long keepAliveTime,
    
13.   TimeUnit unit,
    
14.   BlockingQueue<Runnable> workQueue,
    
15.   ThreadFactory threadFactory) {
    
16.      this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
    
17.           threadFactory, defaultHandler);
    
18.  }
    

20.  public ThreadPoolExecutor(int corePoolSize,
    
21.   int maximumPoolSize,
    
22.   long keepAliveTime,
    
23.   TimeUnit unit,
    
24.   BlockingQueue<Runnable> workQueue,
    
25.   RejectedExecutionHandler handler) {
    
26.      this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
    
27.           Executors.defaultThreadFactory(), handler);
    
28.  }
    

30.  public ThreadPoolExecutor(int corePoolSize,
    
31.   int maximumPoolSize,
    
32.   long keepAliveTime,
    
33.   TimeUnit unit,
    
34.   BlockingQueue<Runnable> workQueue,
    
35.   ThreadFactory threadFactory,
    
36.   RejectedExecutionHandler handler) {
    
37.      if (corePoolSize < 0 ||
    
38.          maximumPoolSize <= 0 ||
    
39.          maximumPoolSize < corePoolSize ||
    
40.          keepAliveTime < 0)
    
41.          throw new IllegalArgumentException();
    
42.      if (workQueue == null || threadFactory == null || handler == null)
    
43.          throw new NullPointerException();
    
44.      this.corePoolSize = corePoolSize;
    
45.      this.maximumPoolSize = maximumPoolSize;
    
46.      this.workQueue = workQueue;
    
47.      this.keepAliveTime = unit.toNanos(keepAliveTime);
    
48.      this.threadFactory = threadFactory;
    
49.      this.handler = handler;
    
50.  }

能夠看到,其須要以下幾個參數:

  • corePoolSize(必需):核心線程數。默認狀況下,核心線程會一直存活,可是當將allowCoreThreadTimeout設置爲true時,核心線程也會超時回收。
  • maximumPoolSize(必需):線程池所能容納的最大線程數。當活躍線程數達到該數值後,後續的新任務將會阻塞。
  • keepAliveTime(必需):線程閒置超時時長。若是超過該時長,非核心線程就會被回收。若是將allowCoreThreadTimeout設置爲true時,核心線程也會超時回收。
  • unit(必需):指定keepAliveTime參數的時間單位。經常使用的有:TimeUnit.MILLISECONDS(毫秒)、TimeUnit.SECONDS(秒)、TimeUnit.MINUTES(分)。
  • workQueue(必需):任務隊列。經過線程池的execute()方法提交的Runnable對象將存儲在該參數中。其採用阻塞隊列實現。
  • threadFactory(可選):線程工廠。用於指定爲線程池建立新線程的方式。
  • handler(可選):拒絕策略。當達到最大線程數時須要執行的飽和策略。

線程池的使用流程以下:

1.  // 建立線程池
    
2.  ThreadPoolExecutor threadPool = new ThreadPoolExecutor(CORE_POOL_SIZE,
    
3.                                               MAXIMUM_POOL_SIZE,
    
4.                                               KEEP_ALIVE,
    
5.                                               TimeUnit.SECONDS,
    
6.                                               sPoolWorkQueue,
    
7.                                               sThreadFactory);
    
8.  // 向線程池提交任務
    
9.  threadPool.execute(new Runnable() {
    
10.      @Override
    
11.      public void run() {
    
12.          ... // 線程執行的任務
    
13.      }
    
14.  });
    
15.  // 關閉線程池
    
16.  threadPool.shutdown(); // 設置線程池的狀態爲SHUTDOWN,而後中斷全部沒有正在執行任務的線程
    
17.  threadPool.shutdownNow(); // 設置線程池的狀態爲 STOP,而後嘗試中止全部的正在執行或暫停任務的線程,並返回等待執行任務的列表

3 線程池的工做原理

下面來描述一下線程池工做的原理,同時對上面的參數有一個更深的瞭解。其工做原理流程圖以下:

經過上圖,相信你們已經對全部參數有個瞭解了。下面再對任務隊列、線程工廠和拒絕策略作更多的說明。

4 線程池的參數

4.1 任務隊列(workQueue)

任務隊列是基於阻塞隊列實現的,即採用生產者消費者模式,在Java中須要實現BlockingQueue接口。但Java已經爲咱們提供了7種阻塞隊列的實現:

  1. ArrayBlockingQueue:一個由數組結構組成的有界阻塞隊列(數組結構可配合指針實現一個環形隊列)。
  2. LinkedBlockingQueue: 一個由鏈表結構組成的有界阻塞隊列,在未指明容量時,容量默認爲Integer.MAX_VALUE
  3. PriorityBlockingQueue: 一個支持優先級排序的無界阻塞隊列,對元素沒有要求,能夠實現Comparable接口也能夠提供Comparator來對隊列中的元素進行比較。跟時間沒有任何關係,僅僅是按照優先級取任務。
  4. DelayQueue:相似於PriorityBlockingQueue,是二叉堆實現的無界優先級阻塞隊列。要求元素都實現Delayed接口,經過執行時延從隊列中提取任務,時間沒到任務取不出來。
  5. SynchronousQueue: 一個不存儲元素的阻塞隊列,消費者線程調用take()方法的時候就會發生阻塞,直到有一個生產者線程生產了一個元素,消費者線程就能夠拿到這個元素並返回;生產者線程調用put()方法的時候也會發生阻塞,直到有一個消費者線程消費了一個元素,生產者纔會返回。
  6. LinkedBlockingDeque: 使用雙向隊列實現的有界雙端阻塞隊列。雙端意味着能夠像普通隊列同樣FIFO(先進先出),也能夠像棧同樣FILO(先進後出)。
  7. LinkedTransferQueue: 它是ConcurrentLinkedQueueLinkedBlockingQueueSynchronousQueue的結合體,可是把它用在ThreadPoolExecutor中,和LinkedBlockingQueue行爲一致,可是是無界的阻塞隊列。

注意有界隊列和無界隊列的區別:若是使用有界隊列,當隊列飽和時並超過最大線程數時就會執行拒絕策略;而若是使用無界隊列,由於任務隊列永遠均可以添加任務,因此設置maximumPoolSize沒有任何意義。

4.2 線程工廠(threadFactory)

線程工廠指定建立線程的方式,須要實現ThreadFactory接口,並實現newThread(Runnable r)方法。該參數能夠不用指定,Executors框架已經爲咱們實現了一個默認的線程工廠:

1.  /**
    
2.   * The default thread factory.
    
3.   */
    
4.  private static class DefaultThreadFactory implements ThreadFactory {
    
5.      private static final AtomicInteger poolNumber = new AtomicInteger(1);
    
6.      private final ThreadGroup group;
    
7.      private final AtomicInteger threadNumber = new AtomicInteger(1);
    
8.      private final String namePrefix;
    

10.      DefaultThreadFactory() {
    
11.          SecurityManager s = System.getSecurityManager();
    
12.          group = (s != null) ? s.getThreadGroup() :
    
13.                                Thread.currentThread().getThreadGroup();
    
14.          namePrefix = "pool-" +
    
15.                        poolNumber.getAndIncrement() +
    
16.                       "-thread-";
    
17.      }
    

19.      public Thread newThread(Runnable r) {
    
20.          Thread t = new Thread(group, r,
    
21.                                namePrefix + threadNumber.getAndIncrement(),
    
22.                                0);
    
23.          if (t.isDaemon())
    
24.              t.setDaemon(false);
    
25.          if (t.getPriority() != Thread.NORM_PRIORITY)
    
26.              t.setPriority(Thread.NORM_PRIORITY);
    
27.          return t;
    
28.      }
    
29.  }

4.3 拒絕策略(handler)

當線程池的線程數達到最大線程數時,須要執行拒絕策略。拒絕策略須要實現RejectedExecutionHandler接口,並實現rejectedExecution(Runnable r, ThreadPoolExecutor executor)方法。不過Executors框架已經爲咱們實現了4種拒絕策略:

  1. AbortPolicy(默認):丟棄任務並拋出RejectedExecutionException異常。
  2. CallerRunsPolicy:由調用線程處理該任務。
  3. DiscardPolicy:丟棄任務,可是不拋出異常。能夠配合這種模式進行自定義的處理方式。
  4. DiscardOldestPolicy:丟棄隊列最先的未處理任務,而後從新嘗試執行任務。

5 功能線程池

嫌上面使用線程池的方法太麻煩?其實Executors已經爲咱們封裝好了4種常見的功能線程池,以下:

  • 定長線程池(FixedThreadPool)
  • 定時線程池(ScheduledThreadPool )
  • 可緩存線程池(CachedThreadPool)
  • 單線程化線程池(SingleThreadExecutor)

5.1 定長線程池(FixedThreadPool)

建立方法的源碼:

1.  public static ExecutorService newFixedThreadPool(int nThreads) {
    
2.      return new ThreadPoolExecutor(nThreads, nThreads,
    
3.                                    0L, TimeUnit.MILLISECONDS,
    
4.                                    new LinkedBlockingQueue<Runnable>());
    
5.  }
    
6.  public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
    
7.      return new ThreadPoolExecutor(nThreads, nThreads,
    
8.                                    0L, TimeUnit.MILLISECONDS,
    
9.                                    new LinkedBlockingQueue<Runnable>(),
    
10.                                    threadFactory);
    
11.  }
  • 特色:只有核心線程,線程數量固定,執行完當即回收,任務隊列爲鏈表結構的有界隊列。
  • 應用場景:控制線程最大併發數。

使用示例:

1.  // 1. 建立定長線程池對象 & 設置線程池線程數量固定爲3
    
2.  ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
    
3.  // 2. 建立好Runnable類線程對象 & 需執行的任務
    
4.  Runnable task =new Runnable(){
    
5.    public void run() {
    
6.       System.out.println("執行任務啦");
    
7.    }
    
8.  };
    
9.  // 3. 向線程池提交任務
    
10.  fixedThreadPool.execute(task);

5.2 定時線程池(ScheduledThreadPool )

建立方法的源碼:

1.  private static final long DEFAULT_KEEPALIVE_MILLIS = 10L;
    

3.  public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    
4.      return new ScheduledThreadPoolExecutor(corePoolSize);
    
5.  }
    
6.  public ScheduledThreadPoolExecutor(int corePoolSize) {
    
7.      super(corePoolSize, Integer.MAX_VALUE,
    
8.            DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
    
9.            new DelayedWorkQueue());
    
10.  }
    

12.  public static ScheduledExecutorService newScheduledThreadPool(
    
13.          int corePoolSize, ThreadFactory threadFactory) {
    
14.      return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
    
15.  }
    
16.  public ScheduledThreadPoolExecutor(int corePoolSize,
    
17.   ThreadFactory threadFactory) {
    
18.      super(corePoolSize, Integer.MAX_VALUE,
    
19.            DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
    
20.            new DelayedWorkQueue(), threadFactory);
    
21.  }
  • 特色:核心線程數量固定,非核心線程數量無限,執行完閒置10ms後回收,任務隊列爲延時阻塞隊列。
  • 應用場景:執行定時或週期性的任務。

使用示例:

1.  // 1. 建立 定時線程池對象 & 設置線程池線程數量固定爲5
    
2.  ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
    
3.  // 2. 建立好Runnable類線程對象 & 需執行的任務
    
4.  Runnable task =new Runnable(){
    
5.    public void run() {
    
6.       System.out.println("執行任務啦");
    
7.    }
    
8.  };
    
9.  // 3. 向線程池提交任務
    
10.  scheduledThreadPool.schedule(task, 1, TimeUnit.SECONDS); // 延遲1s後執行任務
    
11.  scheduledThreadPool.scheduleAtFixedRate(task,10,1000,TimeUnit.MILLISECONDS);// 延遲10ms後、每隔1000ms執行任務

5.3 可緩存線程池(CachedThreadPool)

建立方法的源碼:

1.  public static ExecutorService newCachedThreadPool() {
    
2.      return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
    
3.                                    60L, TimeUnit.SECONDS,
    
4.                                    new SynchronousQueue<Runnable>());
    
5.  }
    
6.  public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
    
7.      return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
    
8.                                    60L, TimeUnit.SECONDS,
    
9.                                    new SynchronousQueue<Runnable>(),
    
10.                                    threadFactory);
    
11.  }
  • 特色:無核心線程,非核心線程數量無限,執行完閒置60s後回收,任務隊列爲不存儲元素的阻塞隊列。
  • 應用場景:執行大量、耗時少的任務。

使用示例:

1.  // 1. 建立可緩存線程池對象
    
2.  ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
    
3.  // 2. 建立好Runnable類線程對象 & 需執行的任務
    
4.  Runnable task =new Runnable(){
    
5.    public void run() {
    
6.       System.out.println("執行任務啦");
    
7.    }
    
8.  };
    
9.  // 3. 向線程池提交任務
    
10.  cachedThreadPool.execute(task);

5.4 單線程化線程池(SingleThreadExecutor)

建立方法的源碼:

1.  public static ExecutorService newSingleThreadExecutor() {
    
2.      return new FinalizableDelegatedExecutorService
    
3.          (new ThreadPoolExecutor(1, 1,
    
4.                                  0L, TimeUnit.MILLISECONDS,
    
5.                                  new LinkedBlockingQueue<Runnable>()));
    
6.  }
    
7.  public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
    
8.      return new FinalizableDelegatedExecutorService
    
9.          (new ThreadPoolExecutor(1, 1,
    
10.                                  0L, TimeUnit.MILLISECONDS,
    
11.                                  new LinkedBlockingQueue<Runnable>(),
    
12.                                  threadFactory));
    
13.  }
  • 特色:只有1個核心線程,無非核心線程,執行完當即回收,任務隊列爲鏈表結構的有界隊列。
  • 應用場景:不適合併發但可能引發IO阻塞性及影響UI線程響應的操做,如數據庫操做、文件操做等。

使用示例:

1.  // 1. 建立單線程化線程池
    
2.  ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
    
3.  // 2. 建立好Runnable類線程對象 & 需執行的任務
    
4.  Runnable task =new Runnable(){
    
5.    public void run() {
    
6.       System.out.println("執行任務啦");
    
7.    }
    
8.  };
    
9.  // 3. 向線程池提交任務
    
10.  singleThreadExecutor.execute(task);

5.5 對比

6 總結

Executors的4個功能線程池雖然方便,但如今已經不建議使用了,而是建議直接經過使用ThreadPoolExecutor的方式,這樣的處理方式讓寫的同窗更加明確線程池的運行規則,規避資源耗盡的風險。

其實Executors的4個功能線程有以下弊端:

  • FixedThreadPoolSingleThreadExecutor:主要問題是堆積的請求處理隊列均採用LinkedBlockingQueue,可能會耗費很是大的內存,甚至OOM。
  • CachedThreadPoolScheduledThreadPool:主要問題是線程數最大數是Integer.MAX_VALUE,可能會建立數量很是多的線程,甚至OOM。
相關文章
相關標籤/搜索