1、CountDownLatch (门栓锁)
CountDownLatch是一个同步工具类,用来协调多个线程之间的同步,或者说起到线程之间的通信(而不是用作互斥的作用)。
CountDownLatch能够使一个线程在等待另外一些线程完成各自工作之后,再继续执行。使用一个计数器进行实现。计数器初始值为线程的数量。当每一个线程完成自己任务后,计数器的值就会减一。当计数器的值为0时,表示所有的线程都已经完成一些任务,然后在CountDownLatch上等待的线程就可以恢复执行接下来的任务。
CountDownLatch的用法:
①、某一线程在开始运行前等待n个线程执行完毕。将CountDownLatch的计数器初始化为new CountDownLatch(n),每当一个任务线程执行完毕,就将计数器减1 countdownLatch.countDown(),当计数器的值变为0时,在CountDownLatch上await()的线程就会被唤醒。一个典型应用场景就是启动一个服务时,主线程需要等待多个组件加载完毕,之后再继续执行。
②、实现多个线程开始执行任务的最大并行性。注意是并行性,不是并发,强调的是多个线程在某一时刻同时开始执行。类似于赛跑,将多个线程放到起点,等待发令枪响,然后同时开跑。做法是初始化一个共享的CountDownLatch(1),将其计算器初始化为1,多个线程在开始执行任务前首先countdownlatch.await(),当主线程调用countDown()时,计数器变为0,多个线程同时被唤醒。
CountDownLatch的不足:CountDownLatch是一次性的,计算器的值只能在构造方法中初始化一次,之后没有任何机制再次对其设置值,当CountDownLatch使用完毕后,它不能再次被使用。
2、CyclicBarrier
①、CyclicBarrier和CountDownLatch比较类似。
区别:CountDownLatch计数器只能使用一次,CyclicBarrier计数器可以通过reset方法重置。CyclicBarrier可以查看阻塞线程数量(getNumberWaiting),判断线程是否被阻塞(isBroken)的方法。
②、 构造函数
函数parties参数指的是,等待多少个线程进入屏障点,即等待多少线程调用await方法,才算所有线程都达到屏障点。函数barrierAction指的是当所有线程达到这个屏障以后,将执行此barrierAction的内容。
③、await挂起当前线程,直到所有线程都达到屏障点时再继续执行。支持让线程等待一定的时间,如果到时间以后还有线程没有达到屏障点,那么让已经达到屏障点的线程继续执行。
④、dowait首先检查是否所有线程都达到屏障状态了,如果是,那么执行构造函数第二个参数barrierAction执行的任务。如果不是所有线程都达到屏障状态,那么当前线程挂起。如果线程挂起时指定了挂起时间,那么当时间到以后,此线程被唤醒,接着唤醒此时已经达到屏障状态的线程。
使用Demo链接地址
3、MarriagePhaser
CountDownLatch和CyclicBarrier的综合体,是栅栏,但不是循环的,而是分阶段的,每个阶段都有不同的线程可以走,但有的线程到了某个阶段就停止了。每个阶段可以有不同数量的线程等待前进到另一个阶段。线程通过调用 arriAndAwaitAdvance() 来阻止它到达屏障,这是一种阻塞方法。当数量到达等于注册的数量时,程序的执行将继续,并且数量将增加。当线程完成其工作时,我们应该调用arrivalAndDeregister()方法来表示在此特定阶段不再考虑当前线程。
使用Demo链接地址
4、ReadWriteLock
①、Java并发库中ReetrantReadWriteLock实现了ReadWriteLock接口并添加了可重入的特性
②、ReetrantReadWriteLock读写锁的效率明显高于synchronized关键字
③、ReetrantReadWriteLock读写锁的实现中,读锁使用共享模式;写锁使用独占模式,换句话说,读锁可以在没有写锁的时候被多个线程同时持有,写锁是独占的
④、ReetrantReadWriteLock读写锁的实现中,需要注意的,当有读锁时,写锁就不能获得;而当有写锁时,除了获得写锁的这个线程可以获得读锁外,其他线程不能获得读锁
使用Demo链接地址
5、Semaphore
Semaphore用于限制可以访问某些资源(物理或逻辑的)的线程数目,他维护了一个许可证集合,有多少资源需要限制就维护多少许可证集合,假如这里有N个资源,那就对应于N个许可证,同一时刻也只能有N个线程访问。一个线程获取许可证就调用acquire方法,用完了释放资源就调用release方法。
例如:假如有3个窗口可以打饭,同一时刻也只能有3名同学打饭。第四个人来了之后就必须在外面等着,只要有打饭的同学好了,就可以去相应的窗口了。
对于Semaphore来说,我们需要记住的其实是资源的互斥而不是资源的同步,在同一时刻是无法保证同步的,但是却可以保证资源的互斥。
①、acquire(int permits)
从此信号量获取给定数目的许可,在提供这些许可前一直将线程阻塞,或者线程已被中断。就好比是一个学生占两个窗口。这同时也对应了相应的release方法。
②、release(int permits)
释放给定数目的许可,将其返回到信号量。这个是对应于上面的方法,一个学生占几个窗口完事之后还要释放多少
③、availablePermits()
返回此信号量中当前可用的许可数。也就是返回当前还有多少个窗口可用。
④、reducePermits(int reduction)
根据指定的缩减量减小可用许可的数目。
⑤、hasQueuedThreads()
查询是否有线程正在等待获取资源。
⑥、getQueueLength()
返回正在等待获取的线程的估计数目。该值仅是估计的数字。
⑦、tryAcquire(int permits, long timeout, TimeUnit unit)
如果在给定的等待时间内此信号量有可用的所有许可,并且当前线程未被中断,则从此信号量获取给定数目的许可。
⑧、acquireUninterruptibly(int permits)
从此信号量获取给定数目的许可,在提供这些许可前一直将线程阻塞。基本上常见的使用方法都在这,Semaphore底层是由AQS和Uasafe完成的
使用Demo链接地址
6、Exchanger
①、Exchanger,并发工具类,用于线程间的数据交换。
②、两个线程,两个缓冲区,一个线程往一个缓冲区里面填数据,另一个线程从另一个缓冲区里面取数据。当填数据的线程将缓冲区填满时,或者取数据的线程将缓冲区里的数据取空时,就主动向对方发起交换缓冲区的动作,而交换的时机是,一个缓冲区满,另一个缓冲区空。
注意:使用Exchanger来对线程进行数据操作时,线程必须是成对的(线程数量为双数)。
③、exchange(V x):等待另一个线程到达此交换点(除非当前线程被中断),然后将给定的对象传送给该线程,并接收该线程的对象。
④、exchange(V x, long timeout, TimeUnit unit):等待另一个线程到达此交换点(除非当前线程被中断,或者超出了指定的等待时间),然后将给定的对象传送给该线程,同时接收该线程的对象。
使用Exchanger可以对两个线程进行多次的数据交换。
使用Exchanger对两个线程进行数据交换时,线程A发送的东西,与线程B接收到的东西是一样的,即连地址都是一样的。
使用Demo链接地址
7、LockSupport
LockSupport主要用于对线程执行暂停(park)和唤醒(unpark)
使用Demo链接地址