java.util.concurrent库提供了一些新的类,以帮助降低编写并发程序的难度,并提高程序的鲁棒性。这些库提供的功能比阻塞队列更复杂,它们都封装了各种阻塞操作。

CountDownLatch(倒记时锁存器)

CountDownLatch对象可用于解决这样的问题:一个任务需要等若干其它任务执行完成之后才能开始执行;需要先执行的任务,在完成任务之后调用CountDownLatch的countDown方法,需要后执行的任务调用await方法,该方法会阻塞,直到CountDownLatch的计数器为0,await方法才会返回。下面的例子演示了CountDownLatch的使用。

例子:CountDownLatch的使用




Java组件Icon_java

Java组件Icon_java_02

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
    }
}



输出:




Java组件Icon_java

Java组件Icon_java_02

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的使用




Java组件Icon_java

Java组件Icon_java_02

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);
    }
}



输出:




Java组件Icon_java

Java组件Icon_java_02

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的使用




Java组件Icon_java

Java组件Icon_java_02

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));
    }
}



输出:




Java组件Icon_java

Java组件Icon_java_02

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的使用




Java组件Icon_java

Java组件Icon_java_02

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));
    }
}



输出:




Java组件Icon_java

Java组件Icon_java_02

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的使用




Java组件Icon_java

Java组件Icon_java_02

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();
    }
}



输出:




Java组件Icon_java

Java组件Icon_java_02

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()方法里面也会试图从池子中获取对象,和归还对象。主要总结以下几点:

  1. Pool<T>类时一个比较通用的类,它完全可以用在实际项目中;
  2. 如果试图多次向池子checkIn,那么多余的checkIn会被忽略

Pool<T>类的checkout是一个volatile类型的数组,它必然是多个任务共享的,对它的所有操作都用synchronized关键字保护了起来,所以其实volatile修饰符是多余的。