> 文章列表 > 共享锁中:Semaphore 、CyclicBarrier 、CountDownLatch的区别是什么?

共享锁中:Semaphore 、CyclicBarrier 、CountDownLatch的区别是什么?

共享锁中:Semaphore 、CyclicBarrier 、CountDownLatch的区别是什么?

目录

下面是一个使用Semaphore实现共享锁的例子:

下面是一个使用CountDownLatch实现等待一组操作完成的例子:

下面是一个使用CyclicBarrier实现等待一组线程达到某个状态后再同时执行的例子:

结论1:

结论2:


下面是一个使用Semaphore实现共享锁的例子:

package cn.net.cdsz.ccb.test;import java.util.concurrent.Semaphore;public class test {private static Semaphore semaphore = new Semaphore(2);public static void main(String[] args) {Runnable task = () -> {try {semaphore.acquire();System.out.println(Thread.currentThread().getName() + " acquired semaphore");Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();} finally {semaphore.release();System.out.println(Thread.currentThread().getName() + " released semaphore");}};new Thread(task, "Thread 1").start();new Thread(task, "Thread 2").start();new Thread(task, "Thread 3").start();}}

在上面的代码中,创建了三个线程,它们都需要获取一个初始值为2的信号量。因为初始值为2,所以前两个线程可以同时获取信号量,而第三个线程需要等待前面两个线程释放信号量之后才能够获取它。运行程序可以看到,前两个线程同时获取到了信号量,并且在获取到信号量之后都阻塞了1秒钟。最后,它们释放了信号量,第三个线程才能够获取它。

下面是一个使用CountDownLatch实现等待一组操作完成的例子:

package cn.net.cdsz.ccb.test;import java.util.concurrent.CountDownLatch;public class test {public static void main(String[] args) throws InterruptedException {int n = 3;CountDownLatch latch = new CountDownLatch(n);Runnable task = () -> {try {Thread.sleep(1000);System.out.println(Thread.currentThread().getName() + " finished its work");} catch (InterruptedException e) {e.printStackTrace();} finally {latch.countDown();}};for (int i = 0; i < n; i++) {new Thread(task, "Thread " + i).start();}latch.await();System.out.println("All threads finished their work");}}

在上面的代码中,创建了三个线程,它们都需要执行一些耗时的操作。在主线程中,通过CountDownLatch等待这三个线程执行完成后再继续执行。

CyclicBarrier用于等待一组线程达到某个状态后再同时执行。它通过维护一个计数器和一个栅栏状态来实现。计数器的初始值由构造函数设置。每个线程需要达到某个状态时,需要通过await()方法等待其他线程都达到这个状态,此时计数器的值就会减1。当计数器的值变为0时,所有线程就会被唤醒,同时继续执行。

下面是一个使用CyclicBarrier实现等待一组线程达到某个状态后再同时执行的例子:

package cn.net.cdsz.ccb.test;import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;public class test {public static void main(String[] args) {int numThreads = 3;CyclicBarrier barrier = new CyclicBarrier(numThreads);for (int i = 0; i < (numThreads+1); i++) {new Thread(() -> {System.out.println(Thread.currentThread().getName() + " is waiting at the barrier.");try {barrier.await();System.out.println(Thread.currentThread().getName() + " has crossed the barrier.");} catch (InterruptedException | BrokenBarrierException e) {e.printStackTrace();}}).start();}}}

我们创建了一个CyclicBarrier对象,并指定了需要等待的线程数量为3。然后我们创建了三个线程,并让它们执行一个任务:打印出自己正在等待栅栏,然后调用await()方法等待其他线程,最后再打印出自己已经通过栅栏。

当所有线程都调用了await()方法后,栅栏就会打开,所有线程就可以同时通过栅栏继续执行后续的代码了。

结论1:

在输出"Thread-3 has crossed the barrier."之后,第4个线程就已经执行完毕退出了,所以不会有"Thread-3 has finished."的输出。这里 CyclicBarrier  体现出跟 Semaphore 的区别,Semaphore 是继续等待,CyclicBarrier  是直接执行不再等待锁

结论2:

CyclicBarrier和CountDownLatch的区别在于,CyclicBarrier要求等待的线程数量是固定的,并且只有所有线程都调用了await()方法之后,才能继续执行后续的代码。而CountDownLatch则是一个计数器,可以任意地增加或减少计数器的值,并且只要计数器的值不为0,等待线程就会一直被阻塞