CountDownLatch
它被用来同步一个或多个任务,强制它们等待其它任务执行的一组操作完成。
你可以向CountDownLatch对象设置一个初始计数值,任何在这个对象上调用await()的方法都将阻塞,直至这个计数值到达0。其它任务在结束其工作时,可以在该对象上调用countDown()来减小这个计数值。CountDownLatch被设计为只触发一次,计数值不能被重置。如果你需要能够重置值的版本,则可以使用CylicBarrier。
调用countDown()的任务在产生这个调用时并没有被阻塞,只有对await()的调用会被阻塞,直至计数值到达0。
CountDownLatch的典型用法是当前程序有n个互相独立的可解决任务,并创建值为n的CountDownLatch。当每个任务完成时,都会在这个锁存器上调用countDown()。等待问题被解决的任务在这个锁存器上调用await(),将它们自己拦住,直到锁存器计数结束。
总结
CountDownLatch模拟了一种多任务阻塞等待-多可解决任务达成某一条件的这一场景。如果只是多任务阻塞等待某一任务,那么直接使用wait和signalAll就可以简单实现了。如果是等待多任务完成,使用wait和signalAll就必须要结合一个计数值及锁来共同实现这种情景。java帮我们抽象出了这种应用场景,解决方案名为CountDownLatch。
CyclicBarrier
CyclicBarrier适用于这样的情况:你希望创建一组任务,它们并行的执行工作,然后在进行下一个步骤之前等待,直至所有任务都完成(看起来有些像join())。它使得所有的并行任务都将在栅栏处队列,因此可以一致地向前移动。
总结
CountDownLatch像有裁判员的田径比赛,要等所有裁判员都到位之后,才能开跑。CyclicBarrier像没有裁判员的比赛,只需要比赛队员各就各位之后就可以直接开跑。试想一下用wait和signal如何实现这种场景,利用一个锁来控制一个计数值的访问,如果这个计数值大于1,就wait到某个对象上(可以是这个计数值对象),然后-1,如果计数值为1,则对该对象signalAll。java帮我们抽象出这种场景,名曰CyclicBarrier。
DelayQueue
这是一个无界的BlockingQueue,用于放置实现了Delayed接口的对象,其中的对象只能在其到期时才能从队列中取出。这种队列是有序的,即队头对象的延迟到期的时间最长。如果没有任何延迟到期,那么就不会有任何头元素,并且poll()将返回null(正因为这样,你不能将null放置到这种队列中)。
Delayed接口有一个方法名为getDelay(),它可以用来告知延迟到期有多长时间,或者延迟在多长时间之前已经到期,这个方法将限制我们去使用TimeUnit类。
总结
对于普通队列来说就是一个先进先出的队列。对于DelayQueue队列的理解可以认为它存在两个队列,一个优先级队列存放还未到期的任务(从队头到队尾的任务,根据到期时间由近到远排序),然后依次将到期的任务放入一个有序队列,这个有序队列就是我们真正take时取的队列。该构件如其名,代表放入该队列的对象自己可以控制自己被取出的时间。
在使用场景上,DelayQueue适用于限制在多少时间之后才执行的任务。这里限制它的是时间,而不是其他线程。
PriorityBlockingQueue
与普通BlockingQueue区别仅在于不以进队列时间评出队列时间,而以优先级高低说了算。一个以时间论英雄,一个以优先级论英雄。
SynchronousQueue
这是一种没有内部容量的阻塞队列,因此每个put()都必须等待一个take(),反之亦然。这就好像是你把一个对象交给某人-没有任何桌子可以放置这个对象,因此只有在这个人伸出手,准备好接收这个对象时,你才能工作。
ScheduledExecutor
ScheduledExecutor提供给你一种可以定期启动线程的方式。你可以使用schedule()(运行一次任务)或者ScheduleAtFixedRate()(每隔规则的时间重复执行任务),你可以将Runnable对象设置为在将来的某个时刻执行。
功能上类似于定时器,在web系统中,使用非常频繁。
Semaphore
正常的锁(来自concurrent.locks或内建的synchronized锁),在任何时刻都只允许一个任务访问一项资源,而计数信号量允许n个任务同时访问这个资源。
实际上,信号量使用较少:1. 资源访问大多只存在1到2的区别,不存在2到多的区别,线程安全的对象多个访问时没有问题,线程不安全的对象两个线程访问就有问题。 2. 如果当前使用场景是只有n个对象提供服务,可以用信号量实现,实际上用阻塞队列可能是更好的做法。
Exchanger
Exchanger是在两个任务之间交换对象的栅栏。当这些任务进入栅栏时,它们各自拥有一个对象,当它们离开时,它们都拥有之前由对象持有的对象。Exchanger的典型应用场景是:一个任务在创建对象,这些对象的生产代价很高昂,而另一个任务在消费这些对象。通过这种方式,可以有更多的对象在创建的同时被消费。