[原创]Java并发编程(5):并发组件库
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修饰符是多余的。
posted on 2013-03-02 01:13 seeker2012 阅读(412) 评论(0) 收藏 举报

浙公网安备 33010602011771号