執行框程序(Executor):最常見的用法就是用Executors來構造相關的線程池;用CompletionService來分離生產任務和已經完成的任務,生產者 submit 執行的任務。使用者 take 已完成的任務,並按照完成這些任務的順序處理它們的結果;Callable和Future接口到處都需要用到。
隊列(Queue):在使用生產者消費者模式的時候需要構建出滿足自己自身系統需要的隊列。
同步器(Synchronizer)常見的有5種:
- 信號量(Semaphore)用於限制可以訪問某些資源(物理的或邏輯的)的線程數目;
- 閉鎖(CountDownLatch)是一個同步輔助類,在完成一組正在其他線程中執行的操作之前,它允許一個或多個線程一直等待。用於在保持給定數目的 信號、事件或條件前阻塞執行。把信號量設置爲1的時候就是一個閉鎖的實現。
- 關卡(CyclicBarrier)允許一個給定數量的成員多次集中在一個關卡點,然後在統一執行後面的操作,這在並行迭代算法中非常有用,能把一個問題拆分成一系列相互獨立的子問題。
- 交換器(Exchanger)是關卡的另一種形式,它提供了一個同步點,在這個同步點,一對線程可以交換數據。每個線程通過exchange()方法的入口提供數據給他的夥伴線程,並接收他的夥伴線程提供的數據,並返回。它在多流水線設計中是有用的。
- Future和FutureTask:可取消的異步計算,利用開始和取消計算的方法、查詢計算是否完成的方法和獲取計算結果的方法。FutureTask可以通過線程池來執行。
併發容器:ConcurrentHashMap來獲取的完全併發和更新的所期望可調整併發的哈希表;期望的讀數和遍歷遠遠大於列表的更新數時,請使用CopyOnWriteArrayList 代替 ArrayList。
原子操作(atomic):支持在單個變量上解除鎖的線程安全編程;我用的最多的是在多線程中的計數操作。
互斥鎖(Lock):爲鎖和等待條件提供一個框架的接口和類,它不同於內置同步和監視器,該框架允許更靈活地使用鎖和條件。
Amdahl定律
Amdahl定律描述了在一個系統中,基於可並行化和串行化的組件各自所佔的比重,程序通過獲得額外的計算資源,理論上能夠加速多少。如果F是必須串行化執行的比重,那麼Amdahl定律告訴我們,在一個N處理器的機器中,我們最多可以加速:1/(F+(1-F)/N) 。
當N無限增大趨近無窮時,speedup的最大值無限趨近1/F,這意味着一個程序中如果50%的處理都需要串行進行的話,speedup只能提升2倍(不考慮事實上有多少線程可用);如果程序的10%需要串行進行,speedup最多能夠提高近10倍。更多信息點擊這裏
Spring中的多線程任務
在中小型項目中把一些提醒類郵件(郵件發送等級較低)放到spring線程池內執行,可以減少郵件發送等待時間。前提是:這些郵件的發送不需要放到數據庫或者是文件系統中持久化。在郵件發送類中引用:TaskExecutor接口來執行郵件發送的線程,spring的配置使用示例:
(一) Latch/Barrier
1. CountDownLatch
我們先來學習一下JDK1.5 API中關於這個類的詳細介紹:
“一個同步輔助類,在完成一組正在其他線程中執行的操作之前,它允許一個或多個線程一直等待。 用給定的計數 初始化 CountDownLatch。由於調用了 countDown() 方法,所以在當前計數到達零之前,await 方法會一直受阻塞。之後,會釋放所有等待的線程,await 的所有後續調用都將立即返回。這種現象只出現一次——計數無法被重置。如果需要重置計數,請考慮使用 CyclicBarrier。”
這就是說,CountDownLatch可以用來管理一組相關的線程執行,只需在主線程中調用CountDownLatch 的await方法(一直阻塞),讓各個線程調用countDown方法。當所有的線程都只需完countDown了,await也順利返回,不再阻塞了。在這樣情況下尤其適用:將一個任務分成若干線程執行,等到所有線程執行完,再進行彙總處理。
下面我舉一個非常簡單的例子。假設我們要打印1-100,最後再輸出“Ok“。1-100的打印順序不要求統一,只需保證“Ok“是在最後出現即可。
解決方案:我們定義一個CountDownLatch,然後開10個線程分別打印(n-1)*10+1至(n-1)*10+10。主線程中調用await方法等待所有線程的執行完畢,每個線程執行完畢後都調用countDown方法。最後再await返回後打印“Ok”。
import java.util.concurrent.CountDownLatch;
/**
* 示例:CountDownLatch的使用舉例
* Mail: [email protected]
* @author janeky
*/
public class TestCountDownLatch {
private static final int N = 10;
public static void main(String[] args) throws InterruptedException {
CountDownLatch doneSignal = new CountDownLatch(N);
CountDownLatch startSignal = new CountDownLatch(1);//開始執行信號
for (int i = 1; i <= N; i++) {
new Thread(new Worker(i, doneSignal, startSignal)).start();//線程啓動了
}
System.out.println("begin------------");
startSignal.countDown();//開始執行啦
doneSignal.await();//等待所有的線程執行完畢
System.out.println("Ok");
}
static class Worker implements Runnable {
private final CountDownLatch doneSignal;
private final CountDownLatch startSignal;
private int beginIndex;
Worker(int beginIndex, CountDownLatch doneSignal,
CountDownLatch startSignal) {
this.startSignal = startSignal;
this.beginIndex = beginIndex;
this.doneSignal = doneSignal;
}
public void run() {
try {
startSignal.await(); //等待開始執行信號的發佈
beginIndex = (beginIndex - 1) * 10 + 1;
for (int i = beginIndex; i <= beginIndex + 10; i++) {
System.out.println(i);
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
doneSignal.countDown();
}
}
}
}