CountDownLatch、CyclicBarrier和Semaphore

1、CountDownLatch用法

类似于计数器,比如某个任务需要等待另外N个任务执行完后再继续执行,就可以用CountDownLatch实现。

构造方法:

//count为计数器值
public
CountDownLatch(int count) { if (count < 0) throw new IllegalArgumentException("count < 0"); this.sync = new Sync(count); }

另外还有三个主要方法:

//调用该方法的线程会进入阻塞状态,等到count值为0时才会继续执行
public void await() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }

//作用同上一个方法,只不过加了超时时间,即使count不为0但超过timeout后也会继续执行
public boolean await(long timeout, TimeUnit unit)
        throws InterruptedException {
        return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
    }
//将计数器count值减一
public void countDown() {
        sync.releaseShared(1);
    }

Demo:比如五个工人干活,工头要等他们一起全部干完活才结算工资

public class Demo {
    public static void main(String[] args) {
        CountDownLatch countDownLatch = new CountDownLatch(5);

        for (int i = 0; i < 5; i ++) {
            final int worker = i;
            new Thread(() -> {
                System.out.println("工人" + worker + "开始工作");
                try{
                    //睡眠模拟干活
                    Thread.sleep(worker * 1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("工人" + worker + "完成工作");
                countDownLatch.countDown();
            }).start();
        }
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("所有工作已完成,工头结算工资");
    }
}

打印结果:

工人0开始工作
工人1开始工作
工人0完成工作
工人4开始工作
工人2开始工作
工人3开始工作
工人1完成工作
工人2完成工作
工人3完成工作
工人4完成工作
所有工作已完成,工头结算工资
View Code

2、CyclicBarrier

它是针对在某一组任务内相互等待,直到这一组任务都执行到某一个设定的点(调用await方法)之后再继续各自执行后续操作。CyclicBarrier是可重用的,CountDownLatch则不行。

构造方法:

//构造参数为每组任务的任务数
public CyclicBarrier(int parties) {
        this(parties, null);
}

//barrierAction:可以在该组任务到达指定点后由最晚到达的线程执行额外的操作
public CyclicBarrier(int parties, Runnable barrierAction) {
        if (parties <= 0) throw new IllegalArgumentException();
        this.parties = parties;
        this.count = parties;
        this.barrierCommand = barrierAction;
}

关键方法:

//调用该方法后线程进入等待,直到该组所有线程都执行到这一句再开始后续操作
public int await() throws InterruptedException, BrokenBarrierException {
        try {
            return dowait(false, 0L);
        } catch (TimeoutException toe) {
            throw new Error(toe); // cannot happen
        }
}

//同上个方法,多加了一个超时时间,超过设定时间直接执行后续操作
public int await(long timeout, TimeUnit unit)
        throws InterruptedException,
               BrokenBarrierException,
               TimeoutException {
        return dowait(true, unit.toNanos(timeout));
    }

Demo:比如体育课老师让学生跑步,全部一起跑完才可以各自自由活动

public class Demo {
    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(3, () -> {
            System.out.println(Thread.currentThread().getName() + "告诉老师,所有人已完成跑步");
        });

        for (int i = 0; i < 3; i ++) {
            final int stu = i;
            new Thread(() -> {
                System.out.println("学生" + stu + "开始跑步");
                try{
                    //睡眠模拟跑步
                    Thread.sleep(2000);
                    System.out.println("学生" + stu + "已到达终点");
                    cyclicBarrier.await();
                } catch (Exception e) {
                    e.printStackTrace();
                }

                System.out.println("所有人到达终点,学生" + stu + "自由活动");

            }, "学生" + stu).start();
        }
    }
}

打印结果:

学生0开始跑步
学生1开始跑步
学生2开始跑步
学生2已到达终点
学生1已到达终点
学生0已到达终点
学生0告诉老师,所有人已完成跑步
所有人到达终点,学生0自由活动
所有人到达终点,学生1自由活动
所有人到达终点,学生2自由活动
View Code

3、Semaphore

信号量,可以控制同时执行的线程数量,类似于锁。

构造方法:

//permits为许可个数,默认为非公平的
public Semaphore(int permits) {
        sync = new NonfairSync(permits);
}

//fair表示是否公平的,公平的就是FIFO
public Semaphore(int permits, boolean fair) {
        sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}

几个关键方法:

//获取一个许可,获取不到时会进入阻塞等待
public void acquire() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
}

//获取permits个许可
public void acquire(int permits) throws InterruptedException {
        if (permits < 0) throw new IllegalArgumentException();
        sync.acquireSharedInterruptibly(permits);
} 

//释放一个许可
public void release() {
        sync.releaseShared(1);
}

//释放permits个许可
public void release(int permits) {
        if (permits < 0) throw new IllegalArgumentException();
        sync.releaseShared(permits);
}

Demo:银行柜台每个服务窗口同一时间只能服务一个顾客,其他顾客只能等待

public class Demo {
    public static void main(String[] args) throws Exception {

        //三个服务窗口
        Semaphore semaphore = new Semaphore(3);

        //9个顾客办理业务
        for (int i = 0; i < 9; i ++) {
            final int customer = i;
            new Thread(() -> {
                try{
                    semaphore.acquire();
                    System.out.println("顾客" + customer + "获得许可,柜台开始服务");

                    //睡眠模拟柜台服务
                    Thread.sleep(2000);
                    semaphore.release();
                    System.out.println("顾客" + customer + "业务已办理完成,释放许可");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

打印结果:

顾客0获得许可,柜台开始服务
顾客4获得许可,柜台开始服务
顾客2获得许可,柜台开始服务
顾客2业务已办理完成,释放许可
顾客0业务已办理完成,释放许可
顾客3获得许可,柜台开始服务
顾客4业务已办理完成,释放许可
顾客1获得许可,柜台开始服务
顾客5获得许可,柜台开始服务
顾客1业务已办理完成,释放许可
顾客5业务已办理完成,释放许可
顾客6获得许可,柜台开始服务
顾客7获得许可,柜台开始服务
顾客3业务已办理完成,释放许可
顾客8获得许可,柜台开始服务
顾客8业务已办理完成,释放许可
顾客6业务已办理完成,释放许可
顾客7业务已办理完成,释放许可
View Code

 

posted @ 2022-02-27 15:57  不速之客  阅读(39)  评论(0编辑  收藏  举报