java 并发组件_[原创]Java并发编程(5):并发组件库

本文介绍了java.util.concurrent库中的并发组件,包括CountDownLatch、CyclicBarrier、semaphore等。通过具体代码示例展示了它们的使用方法,如CountDownLatch可让一个任务等待其他任务完成后执行,CyclicBarrier用于多任务并行执行并在关卡汇合,semaphore可管理对象池。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

CountDownLatch(倒记时锁存器)

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

例子:CountDownLatch的使用

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.pngView 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

}

}

输出:

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.pngView 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的使用

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.pngView 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);

}

}

输出:

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.pngView 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的使用

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.pngView 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));

}

}

输出:

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.pngView 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的使用

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.pngView 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));

}

}

输出:

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.pngView 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的使用

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.pngView 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();

}

}

输出:

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.pngView 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修饰符是多余的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值