JAVA并发-同步工具类CyclicBarrier、CountDownLatch、Semaphore

1.CyclicBarrier

CyclicBarrier初始化时规定一个数目,然后计算调用了CyclicBarrier.await()进入等待的线程数。当线程数达到了这个数目时,所有进入等待状态的线程被唤醒并继续。
功能:可以使一定数量的参与方反复在指定的地方(就是调用await()的地方)汇集(阻塞自己) ,只有所有参与方都调用了await(),栅栏就会打开,所有线程阻塞才会解除

适用场景:CyclicBarrier可以用在所有子线程之间互相等待多次的情形。如并行迭代算法,这种算法通常将一个问题拆分成一系列相互独立的子问题

code

public static void main(String[] args){ //CyclicBarrier 是几个线程导了一个点后等其他线程到了一起出发  哪个地方等就在哪个地方调用await()
    ExecutorService threadpool=Executors.newCachedThreadPool();
    final CyclicBarrier cb=new CyclicBarrier(3);
    for(int i=0;i<3;i++){
        Runnable runnable=new Runnable() {
            
            @Override
            public void run() {
                try {
                    Thread.sleep((long)Math.random()*10000);
                    System.out.println("线程" + Thread.currentThread().getName() +  "即将到达地点1,当前已有"+(cb.getNumberWaiting()+1 )+"个线程");
                    //到达出发点
                    cb.await();
                    Thread.sleep((long)Math.random()*10000);
                    
                    System.out.println("线程"+Thread.currentThread().getName()+"即将到达集合地点2,当前已有"+(cb.getNumberWaiting()+1)+"个线程");
                    
                    cb.await();
                    Thread.sleep((long)Math.random()*10000);
                    System.out.println("线程"+Thread.currentThread().getName()+"即将到达集合地点3,当前已有"+(cb.getNumberWaiting()+1)+"个线程");
                } catch (Exception e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        };
        threadpool.execute(runnable);           
    }
}

2.CountDownLatch(闭锁)

CountDownLatch先初始化规定一个计数器,没调用一次countDwon()计数器减一,如果数量变成0就达到结束状态,闭锁打开(await()方法阻塞解除)

闭锁的作用相当于一扇门,闭锁的状态没有结束时这扇门一直视关闭的,闭锁达到结束状态时将允许所有线程通过

适用场景:CountDownLatch可以应用于主线程等待所有子线程结束后再继续执行的情况。闭锁可以用来确定某些活动直到其他活动都完成了才继续执行
如确保某个服务在其他服务启动之后才能启动;在多玩家的游戏中确保所有玩家就绪后才执行

Code

  public static void main(String[] args){
    ExecutorService threadpool = Executors.newCachedThreadPool();
    final CountDownLatch cdorder=new CountDownLatch(1);
    final CountDownLatch cdanswer=new CountDownLatch(3);
    for(int i=0;i<3;i++){
        Runnable runnable=new Runnable() {
            
            @Override
            public void run() {
                
                try {
                    System.out.println("线程"+Thread.currentThread().getName()+"正准备接受命令");
                    cdorder.await();
                    System.out.println("线程"+Thread.currentThread().getName()+"已接受命令");
                    
                    Thread.sleep((long)Math.random()*10000);
                    System.out.println("线程"+Thread.currentThread().getName()+"回应命令处理结果");
                    cdanswer.countDown();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                
            }
        };
        threadpool.execute(runnable);
    }
    
    try {
        Thread.sleep((long)Math.random()*10000);
        System.out.println("线程"+Thread.currentThread().getName()+"即将发布命令");
        cdorder.countDown();
        System.out.println("线程"+Thread.currentThread().getName()+"已发送命令,正在等待结果");
        cdanswer.await();
        System.out.println("线程"+Thread.currentThread().getName()+"已收到所有响应结果");
        
        
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    threadpool.shutdown();
    
}

CountDownLatch与CyclicBarrier区别

1.CountDownLatch减计数方式
CyclicBarrier 加计数方式

2.CountDownLatch计算为0时释放所有等待的线程
CyclicBarrier 计数达到指定值时释放所有等待线程

3.CountDownLatch计数为0时,无法重置
CyclicBarrier 计数达到指定值时,计数置为0重新开始

4.CountDownLatch调用countDown()方法计数减一,调用await()方法只进行阻塞,对计数没任何影响
CyclicBarrier 调用await()方法计数加1,若加1 的值不等于构造方法的值,则线程阻塞

5.CountDownLatch不可重复利用
CyclicBarrier可重复利用

6.线程在countDown()之后,会继续执行自己的任务,而CyclicBarrier会在所有线程任务结束之后,才会进行后续任务

3.Semaphore(信号量)

计数信号量用来控制同时访问某个特定资源的操作数量,信号量维护了一个许可集。如有必要,在许可可用前会阻塞每一个 acquire(),然后再获取该许可。每个 release() 添加一个许可,从而可能释放一个正在阻塞的获取者。但是,不使用实际的许可对象,Semaphore 只对可用许可的号码进行计数,并采取相应的行动。拿到信号量的线程可以进入代码,否则就等待。通过acquire()和release()获取和释放访问许可。初始值为1的Semaphore二值信号量可以用作互斥体

适用场景:Semaphore可以用于做流量控制,特别公用资源有限的应用场景,比如数据库连接。假如有一个需求,要读取几万个文件的数据,因为都是IO密集型任务,我们可以启动几十个线程并发的读取,但是如果读到内存后,还需要存储到数据库中,而数据库的连接数只有10个,这时我们必须控制只有十个线程同时获取数据库连接保存数据,否则会报错无法获取数据库连接。这个时候,我们就可以使用Semaphore来做流控

Code

public static void main(String[] args){
    
    ExecutorService threadpool = Executors.newCachedThreadPool();

    final Semaphore sp=new Semaphore(3);
    
    
    for(int i=0;i<10;i++){
        Runnable runnable=new Runnable() {
            
            @Override
            public void run() {                 
                    try {
                        sp.acquire();
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    System.out.println("线程" + Thread.currentThread().getName() +" 进入,当前有" + (3- sp.availablePermits())+ " 个线程并发");
                    try {
                        Thread.sleep((long)(Math.random()*10000));
                    } catch (InterruptedException e) {                          
                        e.printStackTrace();
                    }
                    System.out.println("线程"+Thread.currentThread().getName()+"即将离开");
                    sp.release();
                    
                    System.out.println("线程"+Thread.currentThread().getName()+"已离开,当前已有"+(3-sp.availablePermits())+"个线程并发");
            }
        };
        
        threadpool.execute(runnable);
        
    }
    
}

控制任务提交速度

class BoundedExecutor{//使用Semaphore控制任务的提交速率
private final Executor exec;
private final Semaphore semaphore;
public BoundedExecutor(Executor exec,Semaphore semaphore){
    this.exec=exec;
    this.semaphore=semaphore;
}
public void submitTask(final Runnable command) throws InterruptedException{
    semaphore.acquire();
    try{
        exec.execute(new Runnable() {
            
            @Override
            public void run() {
                try{
                    command.run();
                }finally{
                    semaphore.release();
                }
                
            }
        });
    }catch(RejectedExecutionException e){
        semaphore.release();
    }
    }
  }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,542评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,596评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,021评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,682评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,792评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,985评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,107评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,845评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,299评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,612评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,747评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,441评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,072评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,828评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,069评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,545评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,658评论 2 350

推荐阅读更多精彩内容