JUC之各种同步结构(Semaphore、CountDownLatch、CyclicBarrier)

Semaphore

概念

Semaphore是Java版的信号量的实现。它通过控制一定数量的允许(permit)的方式,来达到限制通用资源访问的目的。
类似于在等出租车时,为防止过度拥挤,调度员指挥排队等待坐车的人一次只能进5个人,等此5人走后,再放进下一批。

实现示例代码

Semaphore实现示例
类似于上面提到的等车问题的实现。

public class UsualSemaphoreSample {
    public static void main(String[] args) {
        System.out.println("Action ... Go!");
        Semaphore semaphore = new Semaphore(5);
        for (int i = 0; i < 10; i++) {
            Thread t = new Thread(new SemaphoreWorker(semaphore));
            t.start();
        }
    }
}

 class SemaphoreWorker implements Runnable{
    private String name;
    private Semaphore semaphore;
    public SemaphoreWorker(Semaphore semaphore) {
        this.semaphore = semaphore;
    }
    @Override
    public void run() {
        try {
            log("is waiting for a permit!");
            semaphore.acquire();
            log("acquire a permit");
            log("executed!(执行)");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            log("released a permit!");
            semaphore.release();
        }
    }
    private void log(String msg) {
        if (name == null)
            name = Thread.currentThread().getName();
        System.out.println(name +"  "+ msg);
    }
}

运行结果:
在这里插入图片描述
上述代码只是Semaphore的典型示例,实现逻辑是:线程试图获得许可,得到许可后可进行任务,然后释放许可,这时其他线程就已经可以获得许可进入任务了,从运行结果来看,我们Semaphore的允许机制对工作线程的限制。
想要实现满5人再发车的情况还需要进行代码修改。

符合等出租车的示例代码

public class AbnormalSemaphoreSample {
    public static void main(String[] args) throws InterruptedException{
        Semaphore semaphore = new Semaphore(0);
        for (int i = 0; i < 10; i ++) {
            Thread t = new Thread(new MyWorker(semaphore));
            t.start();
        }
        System.out.println("Action ... GO!");
        semaphore.release(5);
        System.out.println("Waiting for permit off");
        while (semaphore.availablePermits() != 0)
            Thread.sleep(100L);
        System.out.println("Action ... Go ... Again!");
        semaphore.release(5);
    }
}
class MyWorker implements Runnable {
    private Semaphore semaphore;
    public MyWorker(Semaphore semaphore) {
        this.semaphore = semaphore;
    }
    @Override
    public void run() {
        try {
            semaphore.acquire();
            System.out.println("Executed!(执行)");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

运行结果:
在这里插入图片描述
注意:
以上代码只是为了演示功能,很多使用方法是不建议在工程中实用的,例如使用sleep来协调任务执行,而且使用轮询调用 availablePermit() 方法来检测信号量的获取情况。
这都很低效且脆弱,通常只会用在测试或诊断场景中。

总结

Semaphore就是一个计数器,基本逻辑就是acquire和release,没有太过复杂的同步逻辑。

CountDownLatch 和 CyclicBarrier

二者区别

  • CountDownLatch 是不可以重置的,所以不可复用;CyclicBarrier没有这种限制,可以重用。
  • CountDownLatch 的基本造作组合是countDown 和 await 。调用await 的线程阻塞等待countDown 足够的次数,不管你是在一个线程里还是多个线程中countDown,只要次数足够即可。可以说CountDownLatch操作的是 事件
  • CyclicBarrier的基本操作组合就是await。当所有的伙伴(parities)都调用了await,才会继续执行任务,并自动进行重置。注意: 正常情况下CyclicBarrier的重置都是自动发生的,如果我们调用了reset方法,但还有线程在等待,就会导致线程被打扰,抛出BrokenBarrierException。CyclicBarrier侧重点是 线程 而不是事件。它的典型应用场景是用来等待并发线程结束。

CountDownLatch 代码示例

用CountDownLatch实现等出租车排队情况。

public class LatchSample {
    public static void main(String[] args) throws InterruptedException{
        CountDownLatch latch = new CountDownLatch(6);
        for (int i = 0; i < 5; i++) {
            Thread t = new Thread(new FirstBatchWorker(latch));
            t.start();
        }
        for (int i = 0; i < 5; i++){
            Thread t = new Thread(new SecondBatchWorker(latch));
            t.start();
        }
        //注意:这里只是为了以演示为目的,工程中不推荐此协调方法
        while (latch.getCount() != 1)
            Thread.sleep(100L);
        System.out.println("Wait for first Batch finish!");
        latch.countDown();
    }
}

class FirstBatchWorker implements Runnable {
    private CountDownLatch latch;
    public FirstBatchWorker(CountDownLatch latch) {
        this.latch = latch;
    }
    @Override
    public void run() {
        System.out.println("First Batch executed!");
        latch.countDown();
    }
}

class SecondBatchWorker implements Runnable {
    private CountDownLatch latch;
    public SecondBatchWorker(CountDownLatch latch) {
        this.latch = latch;
    }
    @Override
    public void run() {
        try {
            latch.await();
            System.out.println("Second Batch executed!");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

运行结果:
在这里插入图片描述
在实际应用中的条件依赖,往往没有这么别扭,CountDownLatch用于在线程间等待操作结束,是非常简单普遍的用法。通过CountDown 和 await组合通信是非常高效的,工程中不建议用上面循环的那种等待方式。

CyclicBarrier 代码示例

CyclicBarrier 反应的是线程并行运行时的协调,在下面的示例里,从逻辑上5个工作线程更像是代表了5个可以就绪的空车,而不再是5个乘客。

public class CyclicBarrierSample {
    public static void main(String[] args) throws InterruptedException{
        CyclicBarrier barrier = new CyclicBarrier(5, new Runnable() {
            @Override
            public void run() {
                System.out.println("Action ... Go!");
            }
        });
        for (int i = 0; i < 5; i++) {
            Thread t = new Thread(new CyclicWorker(barrier));
            t.start();
        }
    }
    
    static class CyclicWorker implements Runnable {
        private CyclicBarrier barrier;
        public CyclicWorker(CyclicBarrier barrier) {
            this.barrier = barrier;
        }
        @Override
        public void run() {
            try {
                for (int i = 0; i < 3; i++) {
                    System.out.println("Executed!(执行)");
                    barrier.await();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
    }
}

运行结果:
在这里插入图片描述
注意:
为了让输出更能表达运行时序,我们使用了CyclicBarrier特有的barrierAction,当屏障被触发时,Java自动调度该动作。因为CyclicBarrier 会自动进行重置,所以这个逻辑可以非常自然的支持更多排队人数.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值