多线程之旅-JUC工具类(CountDownLatch、CyclicBarrier、Semaphore)

CountDownLatch

  • CountDownLatch主要有两个方法,当一个或多个线程调用await方法时,这些线程会阻塞。
  • 其它线程调用countDown方法会将计数器减1(调用countDown方法的线程不会阻塞),
  • 当计数器的值变为0时,因await方法阻塞的线程会被唤醒,继续执行。
  • 一定要全部线程跑完,最后跑主线程。

例子:教室人走光了才会锁门

public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {
        //倒计时
        CountDownLatch countDownLatch = new CountDownLatch(6);
        for (int i = 1; i <= 6; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "\t离开教室");
                countDownLatch.countDown();
            }, String.valueOf(i)).start();
        }
        //主线程--阻塞等待(如果没有等待,会出现人还没走光,教室的们却锁上了)
        countDownLatch.await();
        System.out.println(Thread.currentThread().getName() + "\t关闭教室锁门" );
    }
}

CyclicBarrier

  • 字面意思是可循环( Cyclic )使用的屏障( Barrier )。它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,
  • 直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续干活。
  • 线程进入屏障通过 CyclicBarrier 的 await() 方法。

例子不恰当:人到齐了开会、七龙珠

//不要看这个例子,看总结
public class CyclicBarrierDemo {
    public static void main(String[] args) {
        //CyclicBarrier(int parties, Runnable barrierAction)
        CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
            System.out.println("*****召唤神龙*****");
        });

        for (int i = 1; i <=7 ; i++) {
            int finalI = i;
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "\t收集到第" + finalI + "颗龙珠");
                try {
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }, String.valueOf(i)).start();
        }
    }
}

Semaphore

  • acquire(获取) 当一个线程调用acquire操作时,它要么通过成功获取信号量(信号量减1),
    要么一直等下去,直到有线程释放信号量,或超时。
  • release(释放)实际上会将信号量的值加1,然后唤醒等待的线程。
  • 信号量主要用于两个目的,一个是用于多个共享资源的互斥使用,另一个用于并发线程数的控制。

例子:抢车位

public class SemaphoreDemo {

    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(3); //模拟资源类,有三个车位
        for (int i = 1; i <= 6 ; i++) {
            new Thread(() -> {
                try {
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName() + "\t抢占到了车位");
                    TimeUnit.SECONDS.sleep(3);
                    System.out.println(Thread.currentThread().getName() + "\t离开了车位");

                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    semaphore.release();
                }
            }, String.valueOf(i)).start();
        }
    }
}

总结

CountDownLatch与CyclicBarrier两者区别:
对于CountDownLatch,当计数为0的时候,下一步的动作实施者是main函数,具有不可重复性;对于CyclicBarrier,下一步动作实施者是其他线程,具有往复多次实施动作的特点。

(转载例子)
模拟了三个玩家,在三个玩家都准备好之后,游戏才能开始

import java.util.Random;
import java.util.concurrent.CountDownLatch;

public class CountDownLatchTest {

    public static void main(String[] args) throws InterruptedException {
        CountDownLatch latch = new CountDownLatch(4);
        for(int i = 0; i < latch.getCount(); i++){
            new Thread(new MyThread(latch), "player"+i).start();
        }
        System.out.println("正在等待所有玩家准备好");
        latch.await();
        System.out.println("开始游戏");
    }

    private static class MyThread implements Runnable{
        private CountDownLatch latch ;

        public MyThread(CountDownLatch latch){
            this.latch = latch;
        }

        @Override
        public void run() {
            try {
                Random rand = new Random();
                int randomNum = rand.nextInt((3000 - 1000) + 1) + 1000;//产生1000到3000之间的随机整数
                Thread.sleep(randomNum);
                System.out.println(Thread.currentThread().getName()+" 已经准备好了, 所使用的时间为 "+((double)randomNum/1000)+"s");
                latch.countDown();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }
}

结果:
正在等待所有玩家准备好
player0 已经准备好了, 所使用的时间为 1.235s
player2 已经准备好了, 所使用的时间为 1.279s
player3 已经准备好了, 所使用的时间为 1.358s
player1 已经准备好了, 所使用的时间为 2.583s
开始游戏

(转载例子)
团建活动内容为翻越三个障碍物,每一个人翻越障碍物所用的时间是不一样的。但是公司要求所有人在翻越当前障碍物之后再开始翻越下一个障碍物,也就是所有人翻越第一个障碍物之后,才开始翻越第二个,以此类推。类比地,每一个员工都是一个“其他线程”。当所有人都翻越的所有的障碍物之后,程序才结束。而主线程可能早就结束了,这里我们不用管主线程。

public class CyclicBarrierTest {
    
    //设置了三个员工和三个障碍物。
    public static void main(String[] args) {
        CyclicBarrier barrier = new CyclicBarrier(3);
        for(int i = 0; i < barrier.getParties(); i++){
            new Thread(new MyRunnable(barrier), "队友"+i).start();
        }
        System.out.println("main function is finished.");
    }


    private static class MyRunnable implements Runnable{
        private CyclicBarrier barrier;

        public MyRunnable(CyclicBarrier barrier){
            this.barrier = barrier;
        }

        @Override
        public void run() {
            for(int i = 0; i < 3; i++) {
                try {
                    Random rand = new Random();
                    int randomNum = rand.nextInt((3000 - 1000) + 1) + 1000;//产生1000到3000之间的随机整数
                    Thread.sleep(randomNum);
                    System.out.println(Thread.currentThread().getName() + ", 通过了第"+i+"个障碍物, 使用了 "+((double)randomNum/1000)+"s");
                    this.barrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
结果
main function is finished.
队友2, 通过了第0个障碍物, 使用了 1.318s
队友0, 通过了第0个障碍物, 使用了 1.475s
队友1, 通过了第0个障碍物, 使用了 2.615s
队友1, 通过了第1个障碍物, 使用了 1.422s
队友0, 通过了第1个障碍物, 使用了 1.736s
队友2, 通过了第1个障碍物, 使用了 2.513s
队友1, 通过了第2个障碍物, 使用了 1.339s
队友0, 通过了第2个障碍物, 使用了 2.274s
队友2, 通过了第2个障碍物, 使用了 2.354s
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值