Executor Framework
併發集合(Concurrent Collection)
同步器(Synchronizer)
一、executor和task優先於線程:
在Java 1.5 中提供了java.util.concurrent包,在這個包中包含了Executor Framework框架,
這是一個很靈活的基於接口的任務執行工具。該框架提供了非常方便的調用方式和強大的功能,
如:
//創建一個單線程執行器對象。
ExecutorService executor = Executors.newSingleThreadExecutor();
//提交一個待執行的任務。
executor.execute(runnable);
//使執行器優雅的終止。
executor.shutdown();
事實上,Executors對象還提供了更多的工廠方法,
如適用於小型服務器的:Executors.newCachedThreadPool()工廠方法
該方法創建的執行器實現類對於小型服務器來說還是比較有優勢的,
因爲在其內部實現中並沒有提供任務隊列,而是直接將任務提交給當前可用的線程,
如果此時沒有可用的線程了,則創建一個新線程來執行該任務。
因此在任務數量較多的大型服務器上,由於該機制創建了大量的工作者線程,
這將會導致系統的整體運行效率下降。對於該種情況,
Executors提供了另外一個工廠方法Executors.newFixedThreadPool(),
該方法創建的執行器實現類的內部提供了任務隊列,用於任務緩衝。
相比於java.util.Timer,該框架也提供了一個更爲高效的執行器實現類,
通過工廠方法Executors.ScheduledThreadPool()可以創建該類。
它提供了更多的內部執行線程,這樣在執行耗時任務是,其定時精度要優於Timer類。
-------------------------------------------
二、併發工具優先於wait和notify:
對於同步器,concurrent包中給出了四種主要的同步器對象:
CountDownLatch、Semaphore、CyclicBarrier和Exchanger。
這裏前兩種比較常用。在該條目中我們只是簡單介紹一個CountDownLatch的優勢。
使用場景:
該類允許一個或者多個線程等待一個或者多個線程來做某些事情。
CountDownLatch的唯一構造函數帶有一個int類型的參數 ,
這個int參數是指允許所有在等待的線程被處理之前,必須在鎖存器上調用countDown方法的次數。
代碼如下:
package countDownLatch_test;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import threadPool.executorDemo.RejectedTaskController;
/**
*
* 同步器對象:CountDownLatch
* 併發工具優先於wait和notify
*/
public class Driver {
/**
* 工作線程數
*/
private static final int WORK_NUM = 5;
public static void main(String[] args) {
//參數 1 爲倒計數次數,即startSignal.countDown() 1 次,startSignal.await()倒計數爲0,線程釋放
CountDownLatch startSignal = new CountDownLatch(1);
//參數 5 爲倒計數次數,即需要doneSignal.countDown()的次數,doneSignal.await()倒計數爲0,線程釋放
CountDownLatch doneSignal = new CountDownLatch(WORK_NUM);
//創建一個擁有5個線程的線程池
ThreadPoolExecutor exe = (ThreadPoolExecutor)Executors.newFixedThreadPool(WORK_NUM);
//添加線程池關閉後出現的異常處理
exe.setRejectedExecutionHandler(new RejectedTaskController());
for (int i = 0; i < 5; ++i) {
//在未來某個時間執行給定的命令
exe.execute(new Worker(i, startSignal, doneSignal));
}
//在工作線程未啓動前做一些事前準備
doSomethingElse();
//倒數一次,startSignal.await()倒計數爲0,線程釋放
startSignal.countDown();
try {
//等待工作線程全部完畢,也就是doneSignal.countDown()爲5次
doneSignal.await();
//工作線程完畢後的事後處理
doSomethingElse();
}
catch (InterruptedException e) {
e.printStackTrace();
}
finally {
//順序關閉
exe.shutdown();
}
}
private static void doSomethingElse() {
System.out.println("doSomethingElse.....");
}
}
package countDownLatch_test;
import java.util.concurrent.CountDownLatch;
public class Worker implements Runnable{
/**
* 工作編號
*/
private int id;
private final CountDownLatch startSignal;
private final CountDownLatch doneSignal;
public Worker(int id ,CountDownLatch startSignal, CountDownLatch doneSignal) {
this.id = id;
this.startSignal = startSignal;
this.doneSignal = doneSignal;
}
public void run() {
try {
//等待倒數到0,如果沒有到達0,就阻塞等待
startSignal.await();
doWork();
//倒數一次
doneSignal.countDown();
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
private void doWork() {
System.out.println("工作 :"+id+" doWork ....");
}
}
public class RejectedTaskController implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
//打印線程信息
System.out.printf("RejectedTaskController: The task %s has been rejected\n", r.toString());
//打印線程池信息
System.out.printf("RejectedTaskController: %s\n", executor.toString());
//如果此執行程序處於在 shutdown 或 shutdownNow 之後正在終止但尚未完全終止的過程中,則返回 true
System.out.printf("RejectedTaskController: Terminating: %s\n", executor.isTerminating());
//如果關閉後所有任務都已完成,則返回 true
System.out.printf("RejectedTaksController: Terminated: %s\n", executor.isTerminated());
}
}
----------------------------------------------
相比於java.util中提供的集合類,java.util.concurrent中提供的併發集合就有更好的併發性,
其性能通常數倍於普通集合,如ConcurrentHashMap等。
換句話說,除非有極其特殊的原因存在,否則在併發的情況下,一定要優先選擇ConcurrentHashMap,
而不是Collections.syschronizedmap或者Hashtable。
java.util.concurrent包中還提供了阻塞隊列,
該隊列極大的簡化了生產者線程和消費者線程模型的編碼工作。