java.util.concurrent库提供了一些新的类,以帮助降低编写并发程序的难度,并提高程序的鲁棒性。这些库提供的功能比阻塞队列更复杂,它们都封装了各种阻塞操作。
CountDownLatch(倒记时锁存器)
CountDownLatch对象可用于解决这样的问题:一个任务需要等若干其它任务执行完成之后才能开始执行;需要先执行的任务,在完成任务之后调用CountDownLatch的countDown方法,需要后执行的任务调用await方法,该方法会阻塞,直到CountDownLatch的计数器为0,await方法才会返回。下面的例子演示了CountDownLatch的使用。
例子:CountDownLatch的使用
View Code
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
class WaitingTask implements Runnable {
private static int counter = 0;
private final int id = counter++;
private final CountDownLatch latch;
WaitingTask(CountDownLatch latch) {
this.latch = latch;
}
public void run() {
try {
latch.await();
System.out.println("Latch barrier passed for " + this);
} catch (InterruptedException ex) {
System.out.println(this + " interrupted");
}
}
public String toString() {
return String.format("WaitingTask %1$-3d ", id);
}
}
class TaskPortion implements Runnable {
private static int counter = 0;
private final int id = counter++;
private static Random rand = new Random(47);
private final CountDownLatch latch;
TaskPortion(CountDownLatch latch) {
this.latch = latch;
}
public void run() {
try {
doWork();
latch.countDown();
} catch (InterruptedException ex) {
// Acceptable way to exit
}
}
public void doWork() throws InterruptedException {
TimeUnit.MILLISECONDS.sleep(rand.nextInt(2000));
System.out.println(this + "completed");
}
public String toString() {
return String.format("%1$-3d ", id);
}
}
public class CountDownLatchDemo {
static final int SIZE = 15;
public static void main(String[] args) throws Exception {
ExecutorService exec = Executors.newCachedThreadPool();
// All must share a single CountDownLatch object:
CountDownLatch latch = new CountDownLatch(SIZE);
for (int i = 0; i < 3; i++)
exec.execute(new WaitingTask(latch));
for (int i = 0; i < SIZE; i++)
exec.execute(new TaskPortion(latch));
System.out.println("Launched all tasks");
exec.shutdown(); // Quit when all tasks complete
}
}
输出:
View Code
Launched all tasks
11 completed
7 completed
9 completed
10 completed
5 completed
8 completed
12 completed
1 completed
13 completed
2 completed
14 completed
6 completed
4 completed
0 completed
3 completed
Latch barrier passed for WaitingTask 0
Latch barrier passed for WaitingTask 2
Latch barrier passed for WaitingTask 1
说明:
例子中启动了三个await()任务,15个countDown()任务,直到15个countDown()任务都执行完成之后,3个await()任务才开始执行。
CyclicBarrier(关卡)
CyclicBarrier用于这种情形:多个任务并行执行,它们在同一个关卡处汇合,而后开始新一轮的并行执行,如此往复。并行执行的任务会执行await()方法,以等待其它任务执行到这一点。下面这个赛马的例子很好的说明了cyclicBarrier的使用。
例子:CyclicBarrier的使用
View Code
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
class Horse implements Runnable {
private static int counter = 0;
private final int id = counter++;
private int strides = 0;
private static Random rand = new Random(47);
private static CyclicBarrier barrier;
public Horse(CyclicBarrier b) {
barrier = b;
}
public synchronized int getStrides() {
return strides;
}
public void run() {
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 new RuntimeException(e);
}
}
public String toString() {
return "Horse " + id + " ";
}
public String tracks() {
StringBuilder s = new StringBuilder();
for (int i = 0; i < getStrides(); i++)
s.append("*");
s.append(id);
return s.toString();
}
}
public class HorseRace {
static final int FINISH_LINE = 75;
private List<Horse> horses = new ArrayList<Horse>();
private ExecutorService exec = Executors.newCachedThreadPool();
private CyclicBarrier barrier;
public HorseRace(int nHorses, final int pause) {
barrier = new CyclicBarrier(nHorses, new Runnable() {
public void run() {
StringBuilder s = new StringBuilder();
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 = new Horse(barrier);
horses.add(horse);
exec.execute(horse);
}
}
public static void main(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;
}
new HorseRace(nHorses, pause);
}
}
输出:
View Code
===========================================================================
**0
**1
*2
**3
*4
**5
*6
……
……
***********************************************************0
****************************************************************1
********************************************************2
****************************************************3
*************************************************************4
***************************************************************************5
*********************************************************************6
Horse 5 won!
说明:
这里只展示了输出内容的一部分。可以在main方法中指定赛马的数量和每一轮比赛之间的暂停时间。默认是7只马,每一轮比赛之间暂停200毫秒。初始化CyclicBarrier对象的时候,指定了并行执行的任务数量,以及到达关卡的时候,需要执行的方法。Horse是Runnable实现类,它就代表赛马的任务,每一个任务统计自己的取得的总成绩。到达关卡之后,执行的内容就是打印出每一只马的成绩,如果其中一只马的成绩超过了终点线(75),那么就终止所有的任务。
DelayQueue(超时队列)
DelayQueue也是一个不限制大小的队列,放入DelayQueue队列中的对象必须实现Delayed接口,队列中的元素会被排序,放在最前面的是超时最长的元素,如果队列中没有一个元素超时,那么从队列中取出的是null。下面的例子演示了DelayQueue的使用。
例子:DelayQueue的使用
View Code
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
class DelayedTask implements Runnable, Delayed {
private static int counter = 0;
private final int id = counter++;
private final int delta;
private final long trigger;
protected static List<DelayedTask> sequence = new ArrayList<DelayedTask>();
public DelayedTask(int delayInMilliseconds) {
delta = delayInMilliseconds;
trigger = System.nanoTime() + TimeUnit.NANOSECONDS.convert(delta, TimeUnit.MILLISECONDS);
sequence.add(this);
}
public long getDelay(TimeUnit unit) {
return unit.convert(trigger - System.nanoTime(), TimeUnit.NANOSECONDS);
}
public int compareTo(Delayed arg) {
DelayedTask that = (DelayedTask) arg;
if (trigger < that.trigger)
return -1;
if (trigger > that.trigger)
return 1;
return 0;
}
public void run() {
System.out.println(this + " ");
}
public String toString() {
return String.format("[%1$-4d]", delta) + " Task " + id;
}
public String summary() {
return "(" + id + ":" + delta + ")";
}
public static class EndSentinel extends DelayedTask {
private ExecutorService exec;
public EndSentinel(int delay, ExecutorService e) {
super(delay);
exec = e;
}
public void run() {
for (DelayedTask pt : sequence) {
System.out.println(pt.summary() + " ");
}
System.out.println();
System.out.println(this + " Calling shutdownNow()");
exec.shutdownNow();
}
}
}
class DelayedTaskConsumer implements Runnable {
private DelayQueue<DelayedTask> q;
public DelayedTaskConsumer(DelayQueue<DelayedTask> q) {
this.q = q;
}
public void run() {
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 class DelayQueueDemo {
public static void main(String[] args) {
Random rand = new Random(47);
ExecutorService exec = Executors.newCachedThreadPool();
DelayQueue<DelayedTask> queue = new DelayQueue<DelayedTask>();
// 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(new DelayedTaskConsumer(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 5 Calling shutdownNow()
Finished DelayedTaskConsumer
说明:
DelayedTask实现了Delayed接口,它有getDelay和compareTo方法。DelayedTask的构造函数需要指定一个超时时间,即在指定的时间后触发事件。getDelay方法用于确定超时的时间,用任务的触发时间减去当前时间就是超时时间。从输出结果上,可以看到指定的延迟时间最短的,会最先执行。
PriorityBlockingQueue
阻塞的优先级队列,它其实是一个没有大小限制的阻塞队列,只不过从这个队列里面取出元素的时候,要去除优先级最高的一个。放到队列里的对象必须实现Comparable接口,以判断优先级高低。下面的例子演示了PriorityBlockingQueue的使用。
例子:PriorityBlockingQueue的使用
View Code
import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.TimeUnit;
class PrioritizedTask implements Runnable, Comparable<PrioritizedTask> {
private Random rand = new Random(47);
private static int counter = 0;
private final int id = counter++;
private final int priority;
protected static List<PrioritizedTask> sequence = new ArrayList<PrioritizedTask>();
public PrioritizedTask(int priority) {
this.priority = priority;
sequence.add(this);
}
public int compareTo(PrioritizedTask arg) {
return priority < arg.priority ? 1 : (priority > arg.priority ? -1 : 0);
}
public void run() {
try {
TimeUnit.MILLISECONDS.sleep(rand.nextInt(250));
} catch (InterruptedException e) {
// Acceptable way to exit
}
//System.out.println(this);
}
public String toString() {
return String.format("[%1$-3d]", priority) + " Task " + id;
}
public String summary() {
return "(" + id + ":" + priority + ")";
}
public static class EndSentinel extends PrioritizedTask {
private ExecutorService exec;
public EndSentinel(ExecutorService e) {
super(-1); // Lowest priority in this program
exec = e;
}
public void run() {
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 implements Runnable {
private PriorityBlockingQueue<Runnable> q;
public PrioritizedTaskConsumer(PriorityBlockingQueue<Runnable> q) {
this.q = q;
}
public void run() {
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 implements Runnable {
private Random rand = new Random(47);
private Queue<Runnable> queue;
private ExecutorService exec;
public PrioritizedTaskProducer(Queue<Runnable> q, ExecutorService e) {
queue = q;
exec = e; // Used for EndSentinel
}
public void run() {
// 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(new PrioritizedTask(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 class PriorityBlockingQueueDemo {
public static void main(String[] args) throws Exception {
Random rand = new Random(47);
ExecutorService exec = Executors.newCachedThreadPool();
PriorityBlockingQueue<Runnable> queue = new PriorityBlockingQueue<Runnable>();
exec.execute(new PrioritizedTaskProducer(queue, exec));
TimeUnit.MILLISECONDS.sleep(250);
exec.execute(new PrioritizedTaskConsumer(queue));
}
}
输出:
View Code
add:0,priority:8
add:1,priority:5
add:2,priority:3
add:3,priority:1
add:4,priority:1
add:5,priority:9
add:6,priority:8
add:7,priority:0
add:8,priority:2
add:9,priority:7
take: [9 ] Task 5
take: [10 ] Task 10
take: [8 ] Task 0
take: [8 ] Task 6
take: [7 ] Task 9
take: [5 ] Task 1
take: [3 ] Task 2
take: [2 ] Task 8
take: [1 ] Task 4
take: [1 ] Task 3
take: [0 ] Task 7
take: [10 ] Task 11
take: [10 ] Task 12
take: [10 ] Task 13
take: [10 ] Task 14
take: [10 ] Task 15
take: [10 ] Task 16
take: [10 ] Task 17
take: [10 ] Task 18
take: [10 ] Task 19
Finished PrioritizedTaskProducer
说明:
有两个线程,一个负责向队列里面添加任务,一个负责取出任务。从输出上可以看到,取出来的任务是按照优先级的高低取出来的。不过得注意一下PrioritizeTask类的compareTo方法,如果当前任务的优先级低于参数中任务的优先级,则返回1,如果当前任务高于参数中任务的优先级,则返回-1,相等则返回0。也就是判断是从参数对象的角度来看的。
semaphore(信号量)
信号量解决了管理对象池子的问题:可用的对象存在一个池子里,想从这个池子里获取对象的话,必须申请一个“许可证”,而这个“许可证”是有限的,如果当前没有可用的“许可证”,那么获取操作就会阻塞,使用完成对象之后,要记得归还许可证。下面的例子演示了semaphore的使用。
例子:semaphore的使用
View Code
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
class Pool<T> {
private int size;
private List<T> items = new ArrayList<T>();
private volatile boolean[] checkedOut;
private Semaphore available;
public Pool(Class<T> classObject, int size) {
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 new RuntimeException(e);
}
}
public T checkOut() throws InterruptedException {
available.acquire();
return getItem();
}
public void checkIn(T x) {
if (releaseItem(x))
available.release();
}
private synchronized T getItem() {
for (int i = 0; i < size; ++i)
if (!checkedOut[i]) {
checkedOut[i] = true;
return items.get(i);
}
return null; // Semaphore prevents reaching here
}
private synchronized boolean releaseItem(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
}
}
class Fat {
private volatile double d; // Prevent optimization
private static int counter = 0;
private final int id = counter++;
public Fat() {
// Expensive, interruptible operation:
for (int i = 1; i < 10000; i++) {
d += (Math.PI + Math.E) / (double) i;
}
}
public void operation() {
System.out.println(this);
}
public String toString() {
return "Fat id: " + id;
}
}
class CheckoutTask<T> implements Runnable {
private static int counter = 0;
private static int checkOutCount = 0;
private final int id = counter++;
private Pool<T> pool;
public CheckoutTask(Pool<T> pool) {
this.pool = pool;
}
public void run() {
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
}
}
public String toString() {
return "CheckoutTask " + id + " ";
}
}
public class SemaphoreDemo {
final static int SIZE = 5;
public static void main(String[] args) throws Exception {
final Pool<Fat> pool = new Pool<Fat>(Fat.class, SIZE);
ExecutorService exec = Executors.newCachedThreadPool();
for (int i = 0; i < SIZE; i++)
exec.execute(new CheckoutTask<Fat>(pool));
System.out.println("All CheckoutTasks created");
List<Fat> list = new ArrayList<Fat>();
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(new Runnable() {
public void run() {
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 0 before checked out
CheckoutTask 2 before checked out
CheckoutTask 2 checked out count2
All CheckoutTasks created
CheckoutTask 2 checked out Fat id: 1
0: main() thread checked out
CheckoutTask 3 before checked out
Fat id: 2
CheckoutTask 1 before checked out
1: main() thread checked out
Fat id: 4
CheckoutTask 3 checked out count3
CheckoutTask 3 checked out Fat id: 3
CheckoutTask 4 before checked out
CheckoutTask 0 checked out count2
CheckoutTask 0 checked out Fat id: 0
CheckoutTask 2 checking in Fat id: 1
CheckoutTask 1 checked out count4
CheckoutTask 1 checked out Fat id: 1
CheckoutTask 3 checking in Fat id: 3
2: main() thread checked out
Fat id: 3
CheckoutTask 0 checking in Fat id: 0
CheckoutTask 4 checked out count5
CheckoutTask 4 checked out Fat id: 0
CheckoutTask 1 checking in Fat id: 1
CheckoutTask 4 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<T>就是一个对象池子,Fat是存放到池子中的对象,ChecnoutTask会从池子中获取对象,使用它,然后归还对象。main()方法里面也会试图从池子中获取对象,和归还对象。主要总结以下几点:
- Pool<T>类时一个比较通用的类,它完全可以用在实际项目中;
- 如果试图多次向池子checkIn,那么多余的checkIn会被忽略
Pool<T>类的checkout是一个volatile类型的数组,它必然是多个任务共享的,对它的所有操作都用synchronized关键字保护了起来,所以其实volatile修饰符是多余的。