java.util.concurrent库提供了一些新的类,以帮助降低编写并发程序的难度,并提高程序的鲁棒性。这些库提供的功能比阻塞队列更复杂,它们都封装了各种阻塞操作。
CountDownLatch(倒记时锁存器)
CountDownLatch对象可用于解决这样的问题:一个任务需要等若干其它任务执行完成之后才能开始执行;需要先执行的任务,在完成任务之后调用CountDownLatch的countDown方法,需要后执行的任务调用await方法,该方法会阻塞,直到CountDownLatch的计数器为0,await方法才会返回。下面的例子演示了CountDownLatch的使用。
例子:CountDownLatch的使用
View Code
importjava.util.Random;importjava.util.concurrent.CountDownLatch;importjava.util.concurrent.ExecutorService;importjava.util.concurrent.Executors;importjava.util.concurrent.TimeUnit;class WaitingTask implementsRunnable {private static int counter = 0;private final int id = counter++;private finalCountDownLatch latch;
WaitingTask(CountDownLatch latch) {this.latch =latch;
}public voidrun() {try{
latch.await();
System.out.println("Latch barrier passed for " + this);
}catch(InterruptedException ex) {
System.out.println(this + " interrupted");
}
}publicString toString() {return String.format("WaitingTask %1$-3d ", id);
}
}class TaskPortion implementsRunnable {private static int counter = 0;private final int id = counter++;private static Random rand = new Random(47);private finalCountDownLatch latch;
TaskPortion(CountDownLatch latch) {this.latch =latch;
}public voidrun() {try{
doWork();
latch.countDown();
}catch(InterruptedException ex) {//Acceptable way to exit
}
}public void doWork() throwsInterruptedException {
TimeUnit.MILLISECONDS.sleep(rand.nextInt(2000));
System.out.println(this + "completed");
}publicString toString() {return String.format("%1$-3d ", id);
}
}public classCountDownLatchDemo {static final int SIZE = 15;public static void main(String[] args) throwsException {
ExecutorService exec=Executors.newCachedThreadPool();//All must share a single CountDownLatch object:
CountDownLatch latch = newCountDownLatch(SIZE);for (int i = 0; i < 3; i++)
exec.execute(newWaitingTask(latch));for (int i = 0; i < SIZE; i++)
exec.execute(newTaskPortion(latch));
System.out.println("Launched all tasks");
exec.shutdown();//Quit when all tasks complete
}
}
输出:
View Code
Launched all tasks11completed7completed9completed10completed5completed8completed12completed1completed13completed2completed14completed6completed4completed0completed3completed
Latch barrier passedfor WaitingTask 0Latch barrier passedfor WaitingTask 2Latch barrier passedfor WaitingTask 1
说明:
例子中启动了三个await()任务,15个countDown()任务,直到15个countDown()任务都执行完成之后,3个await()任务才开始执行。
CyclicBarrier(关卡)
CyclicBarrier用于这种情形:多个任务并行执行,它们在同一个关卡处汇合,而后开始新一轮的并行执行,如此往复。并行执行的任务会执行await()方法,以等待其它任务执行到这一点。下面这个赛马的例子很好的说明了cyclicBarrier的使用。
例子:CyclicBarrier的使用
View Code
importjava.util.ArrayList;importjava.util.List;importjava.util.Random;importjava.util.concurrent.BrokenBarrierException;importjava.util.concurrent.CyclicBarrier;importjava.util.concurrent.ExecutorService;importjava.util.concurrent.Executors;importjava.util.concurrent.TimeUnit;class Horse implementsRunnable {private static int counter = 0;private final int id = counter++;private int strides = 0;private static Random rand = new Random(47);private staticCyclicBarrier barrier;publicHorse(CyclicBarrier b) {
barrier=b;
}public synchronized intgetStrides() {returnstrides;
}public voidrun() {try{while (!Thread.interrupted()) {synchronized (this) {
strides+= rand.nextInt(3); //Produces 0, 1 or 2
}
barrier.await();
}
}catch(InterruptedException e) {//A legitimate way to exit
} catch(BrokenBarrierException e) {//This one we want to know about
throw newRuntimeException(e);
}
}publicString toString() {return "Horse " + id + " ";
}publicString tracks() {
StringBuilder s= newStringBuilder();for (int i = 0; i < getStrides(); i++)
s.append("*");
s.append(id);returns.toString();
}
}public classHorseRace {static final int FINISH_LINE = 75;private List horses = new ArrayList();private ExecutorService exec =Executors.newCachedThreadPool();privateCyclicBarrier barrier;public HorseRace(int nHorses, final intpause) {
barrier= new CyclicBarrier(nHorses, newRunnable() {public voidrun() {
StringBuilder s= newStringBuilder();for (int i = 0; i < FINISH_LINE; i++)
s.append("="); //The fence on the racetrack
System.out.println(s);for(Horse horse : horses)
System.out.println(horse.tracks());for(Horse horse : horses)if (horse.getStrides() >=FINISH_LINE) {
System.out.println(horse+ "won!");
exec.shutdownNow();return;
}try{
TimeUnit.MILLISECONDS.sleep(pause);
}catch(InterruptedException e) {
System.out.println("barrier-action sleep interrupted");
}
}
});for (int i = 0; i < nHorses; i++) {
Horse horse= newHorse(barrier);
horses.add(horse);
exec.execute(horse);
}
}public static voidmain(String[] args) {int nHorses = 7;int pause = 200;if (args.length > 0) { //Optional argument
int n = new Integer(args[0]);
nHorses= n > 0 ?n : nHorses;
}if (args.length > 1) { //Optional argument
int p = new Integer(args[1]);
pause= p > -1 ?p : pause;
}newHorseRace(nHorses, pause);
}
}
输出:
View Code
===========================================================================
**0
**1
*2
**3
*4
**5
*6……
……***********************************************************0
****************************************************************1
********************************************************2
****************************************************3
*************************************************************4
***************************************************************************5
*********************************************************************6Horse5 won!
说明:
这里只展示了输出内容的一部分。可以在main方法中指定赛马的数量和每一轮比赛之间的暂停时间。默认是7只马,每一轮比赛之间暂停200毫秒。初始化CyclicBarrier对象的时候,指定了并行执行的任务数量,以及到达关卡的时候,需要执行的方法。Horse是Runnable实现类,它就代表赛马的任务,每一个任务统计自己的取得的总成绩。到达关卡之后,执行的内容就是打印出每一只马的成绩,如果其中一只马的成绩超过了终点线(75),那么就终止所有的任务。
DelayQueue(超时队列)
DelayQueue也是一个不限制大小的队列,放入DelayQueue队列中的对象必须实现Delayed接口,队列中的元素会被排序,放在最前面的是超时最长的元素,如果队列中没有一个元素超时,那么从队列中取出的是null。下面的例子演示了DelayQueue的使用。
例子:DelayQueue的使用
View Code
importjava.util.ArrayList;importjava.util.List;importjava.util.Random;importjava.util.concurrent.DelayQueue;importjava.util.concurrent.Delayed;importjava.util.concurrent.ExecutorService;importjava.util.concurrent.Executors;importjava.util.concurrent.TimeUnit;class DelayedTask implementsRunnable, Delayed {private static int counter = 0;private final int id = counter++;private final intdelta;private final longtrigger;protected static List sequence = new ArrayList();public DelayedTask(intdelayInMilliseconds) {
delta=delayInMilliseconds;
trigger= System.nanoTime() +TimeUnit.NANOSECONDS.convert(delta, TimeUnit.MILLISECONDS);
sequence.add(this);
}public longgetDelay(TimeUnit unit) {return unit.convert(trigger -System.nanoTime(), TimeUnit.NANOSECONDS);
}public intcompareTo(Delayed arg) {
DelayedTask that=(DelayedTask) arg;if (trigger that.trigger)return 1;return 0;
}public voidrun() {
System.out.println(this + " ");
}publicString toString() {return String.format("[%1$-4d]", delta) + " Task " +id;
}publicString summary() {return "(" + id + ":" + delta + ")";
}public static class EndSentinel extendsDelayedTask {privateExecutorService exec;public EndSentinel(intdelay, ExecutorService e) {super(delay);
exec=e;
}public voidrun() {for(DelayedTask pt : sequence) {
System.out.println(pt.summary()+ " ");
}
System.out.println();
System.out.println(this + " Calling shutdownNow()");
exec.shutdownNow();
}
}
}class DelayedTaskConsumer implementsRunnable {private DelayQueueq;public DelayedTaskConsumer(DelayQueueq) {this.q =q;
}public voidrun() {try{while (!Thread.interrupted())
q.take().run();//Run task with the current thread
} catch(InterruptedException e) {//Acceptable way to exit
}
System.out.println("Finished DelayedTaskConsumer");
}
}public classDelayQueueDemo {public static voidmain(String[] args) {
Random rand= new Random(47);
ExecutorService exec=Executors.newCachedThreadPool();
DelayQueue queue = new DelayQueue();//Fill with tasks that have random delays:
for (int i = 0; i < 5; i++)
queue.put(new DelayedTask(rand.nextInt(5000)));//Set the stopping point
queue.add(new DelayedTask.EndSentinel(5000, exec));
exec.execute(newDelayedTaskConsumer(queue));
}
}
输出:
View Code
[555 ] Task 1[961 ] Task 4[1693] Task 2[1861] Task 3[4258] Task 0(0:4258)
(1:555)
(2:1693)
(3:1861)
(4:961)
(5:5000)
[5000] Task 5Calling shutdownNow()
Finished DelayedTaskConsumer
说明:
DelayedTask实现了Delayed接口,它有getDelay和compareTo方法。DelayedTask的构造函数需要指定一个超时时间,即在指定的时间后触发事件。getDelay方法用于确定超时的时间,用任务的触发时间减去当前时间就是超时时间。从输出结果上,可以看到指定的延迟时间最短的,会最先执行。
PriorityBlockingQueue
阻塞的优先级队列,它其实是一个没有大小限制的阻塞队列,只不过从这个队列里面取出元素的时候,要去除优先级最高的一个。放到队列里的对象必须实现Comparable接口,以判断优先级高低。下面的例子演示了PriorityBlockingQueue的使用。
例子:PriorityBlockingQueue的使用
View Code
importjava.util.ArrayList;importjava.util.List;importjava.util.Queue;importjava.util.Random;importjava.util.concurrent.ExecutorService;importjava.util.concurrent.Executors;importjava.util.concurrent.PriorityBlockingQueue;importjava.util.concurrent.TimeUnit;class PrioritizedTask implements Runnable, Comparable{private Random rand = new Random(47);private static int counter = 0;private final int id = counter++;private final intpriority;protected static List sequence = new ArrayList();public PrioritizedTask(intpriority) {this.priority =priority;
sequence.add(this);
}public intcompareTo(PrioritizedTask arg) {return priority < arg.priority ? 1 : (priority > arg.priority ? -1 : 0);
}public voidrun() {try{
TimeUnit.MILLISECONDS.sleep(rand.nextInt(250));
}catch(InterruptedException e) {//Acceptable way to exit
}//System.out.println(this);
}publicString toString() {return String.format("[%1$-3d]", priority) + " Task " +id;
}publicString summary() {return "(" + id + ":" + priority + ")";
}public static class EndSentinel extendsPrioritizedTask {privateExecutorService exec;publicEndSentinel(ExecutorService e) {super(-1); //Lowest priority in this program
exec =e;
}public voidrun() {int count = 0;for(PrioritizedTask pt : sequence) {
System.out.println(pt.summary());if (++count % 5 == 0)
System.out.println();
}
System.out.println();
System.out.println(this + " Calling shutdownNow()");
exec.shutdownNow();
}
}
}class PrioritizedTaskConsumer implementsRunnable {private PriorityBlockingQueueq;public PrioritizedTaskConsumer(PriorityBlockingQueueq) {this.q =q;
}public voidrun() {try{while (!Thread.interrupted()) {//Use current thread to run the task:
Runnable obj =q.take();
System.out.println("take: " +obj);
obj.run();
}
}catch(InterruptedException e) {//Acceptable way to exit
}
System.out.println("Finished PrioritizedTaskConsumer");
}
}class PrioritizedTaskProducer implementsRunnable {private Random rand = new Random(47);private Queuequeue;privateExecutorService exec;public PrioritizedTaskProducer(Queueq, ExecutorService e) {
queue=q;
exec= e; //Used for EndSentinel
}public voidrun() {//Unbounded queue; never blocks.//Fill it up fast with random priorities:
for (int i = 0; i < 10; i++) {int priority = rand.nextInt(10);
System.out.println("add:" + i + ",priority:" +priority);
queue.add(newPrioritizedTask(priority));
Thread.yield();
}//Trickle in highest-priority jobs:
try{for (int i = 0; i < 10; i++) {//TimeUnit.MILLISECONDS.sleep(250);
queue.add(new PrioritizedTask(10));
}
TimeUnit.MILLISECONDS.sleep(250);
}catch(InterruptedException e) {//Acceptable way to exit
}
System.out.println("Finished PrioritizedTaskProducer");
}
}public classPriorityBlockingQueueDemo {public static void main(String[] args) throwsException {
Random rand= new Random(47);
ExecutorService exec=Executors.newCachedThreadPool();
PriorityBlockingQueue queue = new PriorityBlockingQueue();
exec.execute(newPrioritizedTaskProducer(queue, exec));
TimeUnit.MILLISECONDS.sleep(250);
exec.execute(newPrioritizedTaskConsumer(queue));
}
}
输出:
View Code
add:0,priority:8add:1,priority:5add:2,priority:3add:3,priority:1add:4,priority:1add:5,priority:9add:6,priority:8add:7,priority:0add:8,priority:2add:9,priority:7take: [9 ] Task 5take: [10 ] Task 10take: [8 ] Task 0take: [8 ] Task 6take: [7 ] Task 9take: [5 ] Task 1take: [3 ] Task 2take: [2 ] Task 8take: [1 ] Task 4take: [1 ] Task 3take: [0 ] Task 7take: [10 ] Task 11take: [10 ] Task 12take: [10 ] Task 13take: [10 ] Task 14take: [10 ] Task 15take: [10 ] Task 16take: [10 ] Task 17take: [10 ] Task 18take: [10 ] Task 19Finished PrioritizedTaskProducer
说明:
有两个线程,一个负责向队列里面添加任务,一个负责取出任务。从输出上可以看到,取出来的任务是按照优先级的高低取出来的。不过得注意一下PrioritizeTask类的compareTo方法,如果当前任务的优先级低于参数中任务的优先级,则返回1,如果当前任务高于参数中任务的优先级,则返回-1,相等则返回0。也就是判断是从参数对象的角度来看的。
semaphore(信号量)
信号量解决了管理对象池子的问题:可用的对象存在一个池子里,想从这个池子里获取对象的话,必须申请一个“许可证”,而这个“许可证”是有限的,如果当前没有可用的“许可证”,那么获取操作就会阻塞,使用完成对象之后,要记得归还许可证。下面的例子演示了semaphore的使用。
例子:semaphore的使用
View Code
importjava.util.ArrayList;importjava.util.List;importjava.util.concurrent.ExecutorService;importjava.util.concurrent.Executors;importjava.util.concurrent.Future;importjava.util.concurrent.Semaphore;importjava.util.concurrent.TimeUnit;class Pool{private intsize;private List items = new ArrayList();private volatile boolean[] checkedOut;privateSemaphore available;public Pool(Class classObject, intsize) {this.size =size;
checkedOut= new boolean[size];
available= new Semaphore(size, true);//Load pool with objects that can be checked out:
for (int i = 0; i < size; ++i)try{//Assumes a default constructor:
items.add(classObject.newInstance());
}catch(Exception e) {throw newRuntimeException(e);
}
}public T checkOut() throwsInterruptedException {
available.acquire();returngetItem();
}public voidcheckIn(T x) {if(releaseItem(x))
available.release();
}private synchronizedT getItem() {for (int i = 0; i < size; ++i)if (!checkedOut[i]) {
checkedOut[i]= true;returnitems.get(i);
}return null; //Semaphore prevents reaching here
}private synchronized booleanreleaseItem(T item) {int index =items.indexOf(item);if (index == -1)return false; //Not in the list
if(checkedOut[index]) {
checkedOut[index]= false;return true;
}return false; //Wasn’t checked out
}
}classFat {private volatile double d; //Prevent optimization
private static int counter = 0;private final int id = counter++;publicFat() {//Expensive, interruptible operation:
for (int i = 1; i < 10000; i++) {
d+= (Math.PI + Math.E) / (double) i;
}
}public voidoperation() {
System.out.println(this);
}publicString toString() {return "Fat id: " +id;
}
}class CheckoutTask implementsRunnable {private static int counter = 0;private static int checkOutCount = 0;private final int id = counter++;private Poolpool;public CheckoutTask(Poolpool) {this.pool =pool;
}public voidrun() {try{
System.out.println(this + "before checked out ");
T item=pool.checkOut();
checkOutCount++;
System.out.println(this + "checked out count" +checkOutCount);
System.out.println(this + "checked out " +item);
TimeUnit.SECONDS.sleep(1);
System.out.println(this + "checking in " +item);
pool.checkIn(item);
}catch(InterruptedException e) {//Acceptable way to terminate
}
}publicString toString() {return "CheckoutTask " + id + " ";
}
}public classSemaphoreDemo {final static int SIZE = 5;public static void main(String[] args) throwsException {final Pool pool = new Pool(Fat.class, SIZE);
ExecutorService exec=Executors.newCachedThreadPool();for (int i = 0; i < SIZE; i++)
exec.execute(new CheckoutTask(pool));
System.out.println("All CheckoutTasks created");
List list = new ArrayList();for (int i = 0; i < SIZE; i++) {
Fat f=pool.checkOut();
System.out.println(i+ ": main() thread checked out ");
f.operation();
list.add(f);
}
TimeUnit.SECONDS.sleep(4);
System.out.println("===========所有的对象都已经取出了,无法取出更多了====================");
Future> blocked = exec.submit(newRunnable() {public voidrun() {try{//Semaphore prevents additional checkout,//so call is blocked:
pool.checkOut();
}catch(InterruptedException e) {
System.out.println("checkOut() 被中断了啊");
}
}
});
TimeUnit.SECONDS.sleep(2);
blocked.cancel(true); //Break out of blocked call
System.out.println("Checking in objects in " +list);for(Fat f : list)
pool.checkIn(f);for(Fat f : list)
pool.checkIn(f);//Second checkIn ignored
exec.shutdown();
}
}
输出:
View Code
CheckoutTask 0before checked out
CheckoutTask2before checked out
CheckoutTask2checked out count2
All CheckoutTasks created
CheckoutTask2 checked out Fat id: 1
0: main() thread checked out
CheckoutTask3before checked out
Fat id:2CheckoutTask1before checked out1: main() thread checked out
Fat id:4CheckoutTask3checked out count3
CheckoutTask3 checked out Fat id: 3CheckoutTask4before checked out
CheckoutTask0checked out count2
CheckoutTask0 checked out Fat id: 0CheckoutTask2 checking in Fat id: 1CheckoutTask1checked out count4
CheckoutTask1 checked out Fat id: 1CheckoutTask3 checking in Fat id: 3
2: main() thread checked out
Fat id:3CheckoutTask0 checking in Fat id: 0CheckoutTask4checked out count5
CheckoutTask4 checked out Fat id: 0CheckoutTask1 checking in Fat id: 1CheckoutTask4 checking in Fat id: 0
3: main() thread checked out
Fat id:0
4: main() thread checked out
Fat id:1
===========所有的对象都已经取出了,无法取出更多了====================checkOut() 被中断了啊
Checking in objects in [Fat id:2, Fat id: 4, Fat id: 3, Fat id: 0, Fat id: 1]
说明:
Pool就是一个对象池子,Fat是存放到池子中的对象,ChecnoutTask会从池子中获取对象,使用它,然后归还对象。main()方法里面也会试图从池子中获取对象,和归还对象。主要总结以下几点:
Pool类时一个比较通用的类,它完全可以用在实际项目中;
如果试图多次向池子checkIn,那么多余的checkIn会被忽略
Pool类的checkout是一个volatile类型的数组,它必然是多个任务共享的,对它的所有操作都用synchronized关键字保护了起来,所以其实volatile修饰符是多余的。