Java并发编程之CyclicBarrier、CountDownLatch和Semaphore

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/byhook/article/details/81301734

概述

同步屏障指的是: 让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续干活。

CyclicBarrier

CyclicBarrier的字面意思是可循环使用(Cyclic)的屏障(Barrier)

以一个实例来说明:

public class Solution {

    public static void main(String[] args) {
        int count = 4;
        CyclicBarrier barrier = new CyclicBarrier(count);
        for (int i = 0; i < count; i++) {
            new Worker(barrier).start();
        }
    }

    public static class Worker extends Thread {
        private CyclicBarrier cyclicBarrier;

        public Worker(CyclicBarrier cyclicBarrier) {
            this.cyclicBarrier = cyclicBarrier;
        }

        @Override
        public void run() {
            System.out.println("线程 " + Thread.currentThread().getName() + "正在执行");
            try {
                //以睡眠来模拟写入数据操作
                Thread.sleep(5000);
                System.out.println("线程 " + Thread.currentThread().getName() + "执行完毕");
                cyclicBarrier.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
            System.out.println("所有线程执行完毕");
        }
    }
}

输出:

线程 Thread-0正在执行
线程 Thread-1正在执行
线程 Thread-2正在执行
线程 Thread-3正在执行
线程 Thread-0执行完毕
线程 Thread-2执行完毕
线程 Thread-1执行完毕
线程 Thread-3执行完毕
所有线程执行完毕
所有线程执行完毕
所有线程执行完毕
所有线程执行完毕

内部基于ReentrantLock实现

CountDownLatch

CountDownLatch的计数器只能使用一次,而CyclicBarrier的计数器可以使用reset()方法重置。所以CyclicBarrier`能处理更为复杂的业务场景。例如,如果计算发生错误,可以重置计数器,并让线程重新执行一次。

public class Solution {

    public static void main(String[] args) {

        final CountDownLatch latch = new CountDownLatch(2);

        new Thread(new WorkRunnable(latch)).start();
        new Thread(new WorkRunnable(latch)).start();

        try {
            System.out.println("等待2个子线程执行完毕...");
            latch.await();
            System.out.println("2个子线程已经执行完毕");
            System.out.println("继续执行主线程");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static class WorkRunnable implements Runnable {

        private CountDownLatch latch;

        public WorkRunnable(CountDownLatch countDownLatch) {
            this.latch = countDownLatch;
        }

        @Override
        public void run() {
            try {
                System.out.println("子线程 " + Thread.currentThread().getName() + "正在执行");
                Thread.sleep(3000);
                System.out.println("子线程 " + Thread.currentThread().getName() + "执行完毕");
                latch.countDown();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

输出:

子线程 Thread-0正在执行
等待2个子线程执行完毕...
子线程 Thread-1正在执行
子线程 Thread-0执行完毕
子线程 Thread-1执行完毕
2个子线程已经执行完毕
继续执行主线程

内部基于AQS实现的非公平锁。

Semaphore

Semaphore(信号量)是用来控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源。

假设有6个人在高铁站,排队取票,只有三台机器,可以如下实现:

public class Solution {

    public static void main(String[] args) {
        int count = 6;   //取票人数
        Semaphore semaphore = new Semaphore(3); //取票机器数目
        for (int i = 0; i < count; i++)
            new Worker(i, semaphore).start();
    }

    static class Worker extends Thread {
        private int num;
        private Semaphore semaphore;

        public Worker(int num, Semaphore semaphore) {
            this.num = num;
            this.semaphore = semaphore;
        }

        @Override
        public void run() {
            try {
                semaphore.acquire();
                System.out.println("第" + this.num + "个人正在取票...");
                Thread.sleep(2000);
                System.out.println("第" + this.num + "个人取票完成");
                semaphore.release();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

输出:

扫描二维码关注公众号,回复: 5403940 查看本文章
0个人正在取票...1个人正在取票...2个人正在取票...0个人取票完成
第1个人取票完成
第3个人正在取票...4个人正在取票...2个人取票完成
第5个人正在取票...3个人取票完成
第4个人取票完成
第5个人取票完成

小结

CountDownLatch和CyclicBarrier都能够实现线程之间的等待,只不过它们侧重点不同:

CountDownLatch一般用于某个线程A等待若干个其他线程执行完任务之后,它才执行,不支持重用。
CyclicBarrier一般用于一组线程互相等待至某个状态,然后这一组线程再同时执行,支持重用。

Semaphore其实和锁机制有点类似,它一般用于控制对某组资源的访问权限。

参考

《Java并发编程的艺术》
https://www.cnblogs.com/dolphin0520/p/3920397.html

猜你喜欢

转载自blog.csdn.net/byhook/article/details/81301734