Semaphore
Semaphore semaphore = new Semaphore(5);
semaphore.acquire();
semaphore.acquire(n);
semaphore.tryAcquire();
semaphore.release();
依据同步器的共享计数器模式,先设置计数器为5,线程调用acquire方法申请许可数量,申请成功计数器-n个许可数,失败则阻塞等待。
当用完锁后要调用release方法去释放许可数,计数器会+n。
使用场景
做限流,设置最大并发线程数,每个请求过来可用线程数-1,线程执行完毕可用线程数+1。
CyclicBarrier
CyclicBarrier cyclicBarrier = new CyclicBarrier(5);
cyclicBarrier.await();
cyclicBarrier.await(3,TimeUnit.SECONDS);
cyclicBarrier.reset();
依据同步器的共享模式,先设置计数器为5,线程每次调用await方法,都会将计数器-1,再进入线程阻塞状态,当计数器为0的时候,广播唤醒所有阻塞的线程。
如果有一个阻塞的线程被中断,也会唤醒所有阻塞线程。
如果担心条件不满足,程序一直被阻塞下去怎么办?
可以使用带超时的await(time, TimeUnit)方法,会通过LockSupport.parkNanos先暂停一段时间,当到达超时时间,线程醒来后,中断其他阻塞线程,并在当前线程抛出TimeoutException异常。
也可以使用reset()方法,重置条件,中断所有阻塞的线程。
使用场景
需要等待所有条件都满足,大家才能继续往下执行。例如足球比赛,需要每个分组都比完,才能进入下一轮。
CountDownLatch
CountDownLatch countDownLatch = new CountDownLatch(5);
countDownLatch.countDown();
countDownLatch.await();
依据同步器的共享模式,先设置计数器为5,然后每次调用countDown方法来进行计数器-1,等到计数器为0时,就唤醒被阻塞的线程,即调用了await方法的线程。
使用场景
需要等待所有条件都满足,我才能往下执行。幼儿园老师要等到小朋友都回家了,他才能回去,而小朋友管自己走就行。
CyclicBarrier与CountDownLatch的区别
CyclicBarrier与CountDownLatch虽然都是计数器,都会产生阻塞,但是阻塞的对象不同。CyclicBarrier是大家要等一起等,CountDownLatch是我等你们先走。另一个区别是CyclicBarrier可以重置计数器。