JUC并发编程之Semaphore、CountDownLatch、CyclicBarrier协同工具

Semaphore是一个计数信号量,常用于限制可以访问某些资源的线程数量,即一种用来控制并发量的共享锁。
CountDownLatch是一个倒计数器,起跑信号。
CyclicBarrier是一个循环栅栏,排队摩天轮。
代码示例:
下面分别是三种并发协同工具的使用测试

public class SemaphoreDemo {
    static Semaphore sp = new Semaphore(6);
//    static KaneSemaphore sp = new KaneSemaphore(6);

    public static void main(String[] args) {
        for (int i = 0; i < 12; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        sp.acquire();//获取信号量
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    System.out.println("当前信号量限制...");
                    try {
                        Thread.sleep(2000L);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("执行完了");
                    sp.release();
                }
            }).start();
        }
    }
}

执行结果:

当前信号量限制...
当前信号量限制...
当前信号量限制...
当前信号量限制...
当前信号量限制...
当前信号量限制...
执行完了
当前信号量限制...
执行完了
执行完了
执行完了
当前信号量限制...
当前信号量限制...
当前信号量限制...
执行完了
执行完了
当前信号量限制...
当前信号量限制...
执行完了
执行完了
执行完了
执行完了
执行完了
执行完了
66个执行
public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch latch = new CountDownLatch(6);
//        KaneCountDownLatch latch = new KaneCountDownLatch(6);

        //一种方式
        for (int i = 0; i < 6; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("开始准备...");
                    latch.countDown();//计数减一
                }
            }).start();
            Thread.sleep(1000L);
        }
        latch.await();//每个线程执行一次则-1,当lache为0时开始向下执行,就是这些现场都准备就绪,然后一起去干同一件事

//        //另一种方式
//        for (int i = 0; i < 6; i++) {
//            new Thread(new Runnable() {
//                @Override
//                public void run() {
//                    latch.countDown();
//                    try {
//                        latch.await();
//                        System.out.println("线程:" + Thread.currentThread().getName() + "执行完毕");
//                    } catch (InterruptedException e) {
//                        e.printStackTrace();
//                    }
//                }
//            }).start();
//        }

        System.out.println("开始干活...");
    }
}

执行结果

开始准备...
开始准备...
开始准备...
开始准备...
开始准备...
开始准备...
开始干活...

public class CyclicBarrierDemo {

    public static void main(String[] args) throws InterruptedException {
        CyclicBarrier barrier = new CyclicBarrier(4);
//        KaneCyclicBarrier barrier = new KaneCyclicBarrier(4);

        for (int i = 0; i < 100; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
//                    barrier.await();
                    try {
                        barrier.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } catch (BrokenBarrierException e) {
                        e.printStackTrace();
                    }
                    System.out.println("任务开始执行");
                }
            }).start();
            Thread.sleep(500L);
        }
    }
}

执行结果

任务开始执行
任务开始执行
任务开始执行
任务开始执行
任务开始执行
任务开始执行
任务开始执行
任务开始执行
任务开始执行
任务开始执行
任务开始执行
任务开始执行
...
44个执行

并发场景测试示例

public static void main(String[] args) throws InterruptedException {

        CountDownLatch countDownLatch = new CountDownLatch(10);

        AccessCounter accessCounter = new AccessCounter();

        for (int i = 0; i < 10; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("开始准备...");
                    try {
                        countDownLatch.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    accessCounter.access();
                }
            }).start();

            Thread.sleep(1000L);
            countDownLatch.countDown();
        }

        System.out.println("开始干活...");
        System.out.println("accessCounter------------------" + accessCounter.getAccessCount());

        for (int i = 0; i < 10; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("开始准备...");
                    try {
                        countDownLatch.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    accessCounter.atomicAccess();
                }
            }).start();

            Thread.sleep(1000L);
            countDownLatch.countDown();
        }

        System.out.println("开始干活...");
        System.out.println("atomicCount------------------" + accessCounter.getAtomicCount());

    }

测试结果

开始准备...
开始准备...
开始准备...
开始准备...
开始准备...
开始准备...
开始准备...
开始准备...
开始准备...
开始准备...
开始干活...
accessCounter------------------3
开始准备...
开始准备...
开始准备...
开始准备...
开始准备...
开始准备...
开始准备...
开始准备...
开始准备...
开始准备...
开始干活...
atomicCount------------------10

CountDownLatch应用场景和CycleBarrier应用场景

  • CountDownLatch是一个同步的辅助类,允许一个或多个线程,等待其他一组线程完成操作,再继续执行。
  • CycleBarrier是一个同步的辅助类,允许一组线程相互之间等待,达到一个共同点,再继续执行。

CountDownLatch可以理解成倒计时锁,一个线程 等待其他线程执行 countDown(),阻塞的是单个进程,对执行 countDown 的线程几乎没有什么影响。
1)计数值为 1 的CountDownLatch可以用作简单的开/关锁或门:当每个线程都调用了CountDownLatch的await方法后,这些线程都在门处等待,直到另外一个线程调用了该CountDownLatch的countDown方法 。
2) 初始化计数值为N的CountDownLatch可用于使一个线程等待,直到N 个线程完成某个操作,或者某个操作已完成 N 次的场景。
CountDownLatch的一个特性是它不需要让调用了countDown方法的线程等待CountDownLatch的计数达到零,该线程可以继续执行;它只是通过await阻止任何线程,直到所有线程都可以通过。
场景1,高考ing,监考老师发下去试卷,等待学生答题,有的学生提前交卷了,有的学生最后时间到了才交卷,老师到时间后收卷,贴封条,下班回家。
场景2,屌丝坐长途客车去城里,早早起床刷牙,为了能赶上客车,并顺利的第2个上车,可客车不发车,一直等到客车上满人了这才发车。

CycleBarrier可以理解成是个障碍,所有线程必须到齐后才能一起通过该障碍,所有线程都被阻塞。
场景1,公司团建,其中一个项目就是全体员工在完成其他项目时,到达一个高达4米的高墙,要求所有人一个不能少的越过高墙,才能继续其他项目。
场景2,玩LOL,出现是个人不同加载状态,最后一个人由于个人原因始终无法加载100%,于是等待所有人状态都加载完毕才开始。

CountDownLatch示例

public static void main(String[] args) {
        CountDownLatch countDownLatch = new CountDownLatch(3);
        IntStream.range(0, 3).forEach(i -> {
            new Thread(() -> {
                try {
                    Thread.sleep(3000);
                    System.out.println("hi ni hao");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    countDownLatch.countDown();
                }
            }).start();
        });

        System.out.println("任务线程启动完成");
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("main线程启动完成");
    }

上述案例中启动了三个任务线程,运行可以发现,任务线程启动完成后主线程陷入等待,直到每个任务线程调用了countDownLatch的countDown方法,使得总的countDown计数到0,主线程才被唤醒继续执行。

CyclicBarrier示例

public static void main(String[] args) {
         CyclicBarrier cyclicBarrier = new CyclicBarrier(3);
         for (int i = 0; i < 3; i++) {
             int finalI = i;
             new Thread(() -> {
                 try {
                     System.out.println(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS")) + " hello " + finalI);
                     long t = (long) (Math.random() * 2000);//模拟执行耗时
                     System.out.println("t=" + t);
                     Thread.sleep((t));
                     cyclicBarrier.await();//在屏障前等待,直到所有线程到达屏障处
                     System.out.println(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS")) + " world " + finalI);
                } catch (Exception e) {
                     e.printStackTrace();
                }
            }).start();
        }
    }

线程调用了CyclicBarrier的await方法后会陷入等待,直到所有各方都在此CyclicBarrier上调用了await方法 。如果当前线程不是最后一个到达屏障处的线程,那么它会出于线程调度目的而被禁用并处于休眠状态。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值