Java線程池總結和經常使用開源庫的使用

new Thread的弊端

執行一個異步任務你還只是以下new Thread嗎?java

new Thread(new Runnable() {

	@Override
	public void run() {
		// TODO Auto-generated method stub
	}
}).start();
複製代碼

new Thread的弊端以下:數據庫

  • 每次new Thread新建對象性能差。
  • 線程缺少統一管理,可能無限制新建線程,相互之間競爭,及可能佔用過多系統資源致使死機或oom。
  • 缺少更多功能,如定時執行、按期執行、線程中斷。 相比new Thread,Java提供的四種線程池的好處在於:
  • 重用存在的線程,減小對象建立、消亡的開銷,性能佳。
  • 可有效控制最大併發線程數,提升系統資源的使用率,同時避免過多資源競爭,避免堵塞。
  • 提供定時執行、按期執行、單線程、併發數控制等功能。

Java 線程池

Java經過Executors提供四種線程池,分別爲:緩存

  • new CachedThreadPool 建立一個可緩存線程池,若是線程池長度超過處理須要,可靈活回收空閒線程,若無可回收,則新建線程。
  • new FixedThreadPool 建立一個定長線程池,可控制線程最大併發數,超出的線程會在隊列中等待。
  • new ScheduledThreadPool 建立一個定長線程池,支持定時及週期性任務執行。
  • new SingleThreadExecutor 建立一個單線程化的線程池,它只會用惟一的工做線程來執行任務,保證全部任務按照指定順序(FIFO, LIFO, 優先級)執行。

Executors 主要是這個構造函數:安全

public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) 複製代碼

而Executors工廠類一共能夠建立四種類型的線程池,經過Executors.newXXX便可建立。下面就分別都介紹一下把。網絡

1. FixedThreadPool

建立一個定長線程池,可控制線程最大併發數,超出的線程會在隊列中等待。示例代碼以下:併發

ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
for (int i = 0; i < 10; i++) {
	final int index = i;
	fixedThreadPool.execute(new Runnable() {
 
		@Override
		public void run() {
			try {
				System.out.println(index);
				Thread.sleep(2000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	});
}
複製代碼

由於線程池大小爲3,每一個任務輸出index後sleep 2秒,因此每兩秒打印3個數字。異步

定長線程池的大小最好根據系統資源進行設置。ide

原理

public static ExecutorService newFixedThreadPool(int nThreads){
    return new ThreadPoolExecutor(nThreads,nThreads,0L,TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
}
複製代碼
  • 它是一種固定大小的線程池;
  • corePoolSize和maximunPoolSize都爲用戶設定的線程數量nThreads;
  • keepAliveTime爲0,意味着一旦有多餘的空閒線程,就會被當即中止掉;但這裏keepAliveTime無效;
  • 阻塞隊列採用了LinkedBlockingQueue,它是一個無界隊列;
  • 因爲阻塞隊列是一個無界隊列,所以永遠不可能拒絕任務;- 因爲採用了無界隊列,實際線程數量將永遠維持在nThreads,所以maximumPoolSize和keepAliveTime將無效。

2. CachedThreadPool

建立一個可緩存線程池,若是線程池長度超過處理須要,可靈活回收空閒線程,若無可回收,則新建線程。示例代碼以下:函數

ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
	final int index = i;
	try {
		Thread.sleep(index * 1000);
	} catch (InterruptedException e) {
		e.printStackTrace();
	}
 
	cachedThreadPool.execute(new Runnable() {
 
		@Override
		public void run() {
			System.out.println(index);
		}
	});
}
複製代碼
原理

//構造函數
public static ExecutorService newCachedThreadPool(){
    return new ThreadPoolExecutor(0,Integer.MAX_VALUE,60L,TimeUnit.MILLISECONDS,new SynchronousQueue<Runnable>());
}
複製代碼

線程池爲無限大,當執行第二個任務時第一個任務已經完成,會複用執行第一個任務的線程,而不用每次新建線程。性能

  • 它是一個能夠無限擴大的線程池;
  • 它比較適合處理執行時間比較小的任務;corePoolSize爲0,maximumPoolSize爲無限大,意味着線程數量能夠無限大;
  • keepAliveTime爲60S,意味着線程空閒時間超過60S就會被殺死;
  • 採用SynchronousQueue裝等待的任務,這個阻塞隊列沒有存儲空間,這意味着只要有請求到來,就必需要找到一條工做線程處理他,若是當前沒有空閒的線程,那麼就會再建立一條新的線程。

3. SingleThreadExecutor

建立一個單線程化的線程池,它只會用惟一的工做線程來執行任務,保證全部任務按照指定順序(FIFO, LIFO, 優先級)執行。示例代碼以下:

ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
	final int index = i;
	singleThreadExecutor.execute(new Runnable() {

		@Override
		public void run() {
			try {
				System.out.println(index);
				Thread.sleep(2000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	});
}
複製代碼

結果依次輸出,至關於順序執行各個任務。

原理

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

它只會建立一條工做線程處理任務;採用的阻塞隊列爲LinkedBlockingQueue;

4. ScheduledThreadPool

建立一個定長線程池,支持定時及週期性任務執行。延遲執行示例代碼以下:

ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
scheduledThreadPool.schedule(new Runnable() {
 
	@Override
	public void run() {
		System.out.println("delay 3 seconds");
	}
}, 3, TimeUnit.SECONDS);
複製代碼

表示延遲3秒執行。

scheduledThreadPool.scheduleAtFixedRate(new Runnable() {

	@Override
	public void run() {
		System.out.println("delay 1 seconds, and excute every 3 seconds");
	}
}, 1, 3, TimeUnit.SECONDS);
複製代碼

表示延遲1秒後每3秒執行一次。

ScheduledExecutorService比Timer更安全,功能更強大

原理

  • 它接收SchduledFutureTask類型的任務,有兩種提交任務的方式:scheduledAtFixedRate, scheduledWithFixedDelay
  • 它採用DelayQueue存儲等待的任務

DelayQueue內部封裝了一個PriorityQueue,它會根據time的前後時間排序,若time相同則根據sequenceNumber排序;

DelayQueue也是一個無界隊列;

工做線程會從DelayQueue取已經到期的任務去執行;

執行結束後從新設置任務的到期時間,再次放回DelayQueue

最佳實踐:

AsynTask 4.0

  • 默認線程池是 SingleThreadExecutor; 單一串行線程池。
  • 內部還內置一個CPU*2+1的 FixedThreadPool,命名爲:THREAD_POOL_EXECUTOR。

RxJava

  • Schedulers.io( )

默認是一個CachedThreadScheduler,用於IO密集型任務,如異步阻塞IO操做(讀寫文件、讀寫數據庫、網絡信息交互等)所使用的 Scheduler。

  • Schedulers.computation( )

默認是:FixedThreadPool。用於CPU 密集型計算,即不會被 I/O 等操做限制性能的操做,例如圖形的計算,事件循環或和回調處理

  • Schedulers.immediate( )

在當前線程當即開始執行任務,至關於不指定線程。這是默認的 Scheduler

  • AndroidSchedulers.mainThread()

Android專用的,指定的操做將在 Android 主線程運行

  • Schedulers.from(executor)

使用指定的Executor做爲調度器

  • Schedulers.newThread()

老是啓用新線程,並在新線程執行操做。

EventBus:

  • 使用 newCachedThreadPool,實現 前臺線程通知

OKHTTP:

  • 相似 newCachedThreadPool 實現:
executor = new ThreadPoolExecutor(0, 2147483647, 60L, TimeUnit.SECONDS, new SynchronousQueue(), Util.threadFactory("OkHttp ConnectionPool", true));
複製代碼

Fresco

  • Default implementation of SerialExecutorService

參考

  1. 代碼例子參考網絡
  2. 圖片流程圖部分來自知乎截圖
相關文章
相關標籤/搜索