1. 閉鎖(Latch)
閉鎖是一種Synchronizer,它可以延遲線程的進度直到線程達到終止狀態。一個閉鎖工作起來就像一道大門:直到閉鎖達到終點狀態之前,門一直是關閉的,沒有線程能通過,在終點狀態到來的時候,門開了,允許所有線程都通過。一旦閉鎖到達了終點狀態,它就不能夠再改變狀態了,所以它永遠保持敞開狀態。閉鎖可以用來確保特定活動,直到其他的活動完成之後才發生。
CountDownLatch是一個靈活的閉鎖實現。允許一個或多個線程等待一個事件集的發生。閉鎖的狀態包括一個計數器,初始化爲一個正數,用來表現需要等待的事件數。countdown方法對計數器做減操作,表示一個事件已經發生了,而await方法等待計數器達到零,此時所有需要等待的事件都已經發生。如果計數器入口時值爲非零,await會一直阻塞到計數器爲零,或者等待線程中斷以超時。
下面的代碼闡釋了閉鎖的兩種實現方法。
import java.util.concurrent.CountDownLatch;
public class TestHarness {
public long timeTasks(int nThreads, final Runnable task)throws InterruptedException{
final CountDownLatch startGate = new CountDownLatch(1);
final CountDownLatch endGate = new CountDownLatch(nThreads);
for (int i = 0;i < nThreads; ++i){
Thread t = new Thread(){
public void run(){
try{
startGate.await();
try{
task.run();
}finally{
endGate.countDown();
}
} catch(InterruptedException e){
e.printStackTrace();
}
}
};
t.start();
}
long start = System.nanoTime();
startGate.countDown();
endGate.await();
long end = System.nanoTime();
return end - start;
}
public static void main(String[] args){
TestHarness th = new TestHarness();
Runnable task = new Runnable(){
public void run(){
System.out.println("1");
}
};
try {
th.timeTasks(10, task);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("2");
}
}
解釋:TestHarness 創建了一些線程,併發地執行給定的任務。它使用兩個閉鎖,一個“開始閥門”和一個“結束閥門”。這個開始閥門將計數器初始化爲1.結束閥門將計數器初始化爲工作線程的數量。每一個工作線程要做的第一件事就是等待開始閥門打開;這樣做能確保直到所有線程都做好準備時,纔開始工作。每個線程的最後一個工作是爲結束閥門減一;這樣做使控制線程有效地等待,直到最後一個工作線程完成任務,這樣就能計算整個的用時了。
2. FutureTask
FutureTask同樣可以作爲閉鎖。FutureTask的計算是通過Callable實現的,它等同於一個可攜帶結果的Runnable,並且有3個狀態:等待、運行和完成。完成包括所有計算以任意的方式結束,包括正常結束、取消和異常。一旦FutureTask進入完成狀態,它會永遠停止在這個狀態上。
Future.get的行爲依賴於任務的狀態。如果它已經完成,get可以立即得到返回結果,否則會被阻塞直到任務轉爲完成狀態,然後會返回結果或者拋出異常。FutureTask把計算的結果從運行計算的線程傳送到需要這個結果的線程:FutureTask的規約保證了這種傳遞建立在結果的安全發佈基礎之上。
Executor框架利用FutureTask來完成異步任務,並可以用來進行任何潛在的耗時計算,而且可以在真正需要計算結果之前就啓動它們開始計算。如下代碼,Preloader使用了FutureTask來執行一個代價昂貴的計算,結果稍後會被用到;儘早開始計算,你可以減少等待結果所需花費的時間。
package ip2;
import java.util.concurrent.FutureTask;
import java.util.concurrent.Future;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Preloader {
public final FutureTask<String> future = new FutureTask<String>(new Callable<String>(){
public String call() throws Exception {
return loadString();
}
});
private String loadString() throws InterruptedException{
Thread.sleep(1000);
System.out.println("String");
return "String";
}
private final Thread thread = new Thread(future);
public void start(){thread.start();}
public String get() throws InterruptedException, ExecutionException{
return future.get();
}
public static void main(String[] args) throws InterruptedException, ExecutionException{
Preloader p = new Preloader();
ExecutorService e = Executors.newCachedThreadPool();
Future<String> result = e.submit(new Callable<String>(){
public String call() throws Exception {
return p.loadString();
}
});
Thread.sleep(150);
System.out.println(result.cancel(false));
e.shutdown();
}
}
3. 信號量(Semaphore)
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.Semaphore;
public class BoundedHashSet<T> {
private final Set<T> set;
private final Semaphore sem;
public BoundedHashSet(int bound){
this.set = Collections.synchronizedSet(new HashSet<T>());
sem = new Semaphore(bound);
}
public boolean add( T o)throws InterruptedException {
sem.acquire();
boolean wasAdded = false;
try {
wasAdded = set.add(o);
return wasAdded;
}
finally {
if (!wasAdded){
sem.release();
}
}
}
public boolean remove(Object o){
boolean wasRemoved = set.remove(o);
if (wasRemoved){
sem.release();
}
return wasRemoved;
}
}
4. 關卡(CycleBarrier)
關卡類似於閉鎖,它們都能夠阻塞一組線程,直到某些事情發生。其中關卡與閉鎖關鍵的不同在於,所有線程必須同時達到關卡點,才能繼續處理。閉鎖等待的是事件;關卡等待的是其他線程。關卡實現的協議,就像一些家庭成員指定商場中的集合地點:“我們每個人6:00在麥當勞見,到了以後不見不撒,之後我們再決定接下來做什麼。”import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class CelluarAutomata {
private final Board mainBoard;
private final CyclicBarrier barrier;
private final Worker[] workers;
public CelluarAutomata(Board board){
this.mainBoard = board;
int count = Runtime.getRuntime().availableProcessors();
this.barrier = new CyclicBarrier(count, new Runnable(){
public void run(){
mainBoard.commitNewValues();
}
});
this.workers = new Worker[count];
for (int i = 0;i < count ;i++){
workers[i] = new Worker(mainBoard.getSubBoard(count ,i));
}
}
private class Worker implements Runnable{
private final Board board;
public Worker(Board board){
this.board = board;
}
public void run(){
while (!board.hasConverged()){
for (int x = 0; x < board.getMaxX(); x++)
for (int y = 0; y < board.getMaxY();y++)
board.setNewValue(x, y, computeValue(x, y));
try {
barrier.await();
} catch (InterruptedException ex){
return;
} catch (BrokenBarrierException ex){
return;
}
}
}
private Object computeValue(int x, int y) {
// TODO Auto-generated method stub
return null;
}
}
public void start(){
for(int i = 0; i < workers.length; i++){
new Thread(workers[i]).start();
}
mainBoard.waitForConvergence();
}
}