闭锁是一种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.结束阀门将计数器初始化为工作线程的数量。每一个工作线程要做的第一件事就是等待开始阀门打开;这样做能确保直到所有线程都做好准备时,才开始工作。每个线程的最后一个工作是为结束阀门减一;这样做使控制线程有效地等待,直到最后一个工作线程完成任务,这样就能计算整个的用时了。
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 future = new FutureTask(new Callable(){
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 result = e.submit(new Callable(){
public String call() throws Exception {
return p.loadString();
}
});
Thread.sleep(150);
System.out.println(result.cancel(false));
e.shutdown();
}
}
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.Semaphore;
public class BoundedHashSet {
private final Set set;
private final Semaphore sem;
public BoundedHashSet(int bound){
this.set = Collections.synchronizedSet(new HashSet());
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;
}
}
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();
}
}