Java - 併發編程 - 線程池(圖解)

前言

做的學習筆記,並加入了自己的理解,謝謝

使用線程池的原因

我們創建的線程在運行結束後都會被虛擬機銷燬,如果線程數量多的話,頻繁的創建和銷燬線程會大大浪費時間和效率,更重要的是浪費內存,線程池可以讓線程運行後不立刻銷燬,而是讓線程重複使用,繼續執行其他任務

線程池的優化

  1. 降低資源消耗
  2. 提高響應速度
  3. 提高線程的可管理性

流程圖

img

線程池的核心參數

/**
 * 線程核心參數
 * @param corePoolSize    核心線程數量
 * @param maximumPoolSize 最大線程數量
 * @param keepAliveTime   線程空間後的存活時間
 * @param unit            時間單位
 * @param workQueue       用於存放任務的阻塞隊列
 * @param threadFactory   線程工廠類
 * @param handler         當隊列和最大線程池都滿了之後的飽和策略
 */
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler)

線程池可選擇的阻塞隊列

  • 無界隊列
  • 有界隊列 如果超出界定值,將阻塞 put 方法
  • 同步移交隊列 也就是隊列不存儲元素,每個插入操作都要等待另一個線程調用移出操作,否則插入操作一直處於阻塞

無界隊列

ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<>();
for (int i = 0; i < 10; i++) {
  queue.put(i);
  System.out.println("向隊列中添加值:" + i);
}
LinkedBlockingQueue<Integer> queue = new LinkedBlockingQueue<>();
for (int i = 0; i < 10; i++) {
  queue.put(i);
  System.out.println("向隊列中添加值:" + i);
}

有界隊列

ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<>(5);
for (int i = 0; i < 10; i++) {
  queue.put(i);
  System.out.println("向隊列中添加值:" + i);
}
LinkedBlockingQueue<Integer> queue = new LinkedBlockingQueue<>(5);
for (int i = 0; i < 10; i++) {
  queue.put(i);
  System.out.println("向隊列中添加值:" + i);
}

同步移交隊列

SynchronousQueue<Integer> queue = new SynchronousQueue<>();
for (int i = 0; i < 10; i++) {
  // 阻塞在這裏
  queue.put(i);
  System.out.println("向隊列中添加值:" + i);
}
SynchronousQueue<Integer> queue = new SynchronousQueue<>();
// 插入值
new Thread(() -> {
  try {
    queue.put(1);
  } catch (InterruptedException e) {
    e.printStackTrace();
  }
}).start();
// 取值
new Thread(() -> {
  try {
    Integer value = queue.take();
    System.out.println("取到值:" + value);
  } catch (InterruptedException e) {
    e.printStackTrace();
  }
}).start();

線程池可選擇的飽和策略

說明
AbortPolicy 丟棄任務並拋出RejectedExecutionException異常 《默認》
DiscardPolicy 拋棄策略,但是不拋出異常
DiscardOldestPolicy 丟棄隊列最前面的任務(舊任務),然後重新嘗試執行任務
CallerRunsPolicy 由調用線程處理該任務

常用線程池

線程數量無限的線程池

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

線程數量固定線程池

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

單一線程線程池

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

提交任務

submit,獲取結果

ExecutorService threadPool = Executors.newCachedThreadPool();
// 提交任務,並獲取結構
Future<Integer> future = threadPool.submit(() -> {
  Thread.sleep(3000L);
  return 2 * 5;
});
// 阻塞獲取結果
Integer value = future.get();
System.out.println("執行結果:" + value);

execute,沒有結果

ExecutorService threadPool = Executors.newCachedThreadPool();
threadPool.execute(() -> {
  try {
    Thread.sleep(1000L);
  } catch (InterruptedException e) {
    e.printStackTrace();
  }
  // 計算結果
  Integer num = 2 * 3;
  System.out.println("執行結果:" + num);
});

線程池的狀態

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章