JUC之CountDownLatch、Cyclicbarrier、Semaphore

被刻印的时光 ゝ 提交于 2020-02-26 23:38:57

JUC从JDK1.5开始,就是这个包里有很多工具类,这次介绍这三个大头。

CountDownLatch

package com.juc; 
import java.util.concurrent.*;  
public class CountDownLatchTest {  
    public static void main(String[] args) {  
        //主线程 加载游戏资源  
  CountDownLatch count = new CountDownLatch(10);  
        //十个子线程代表十个玩家正在加载游戏  
  ExecutorService exec = Executors.newFixedThreadPool(11);  
        Runnable game = new Runnable() {  
            @Override  
  public void run() {  
                try {  
                    count.await();  
                    System.out.println("全部加载完毕!进入英雄联盟!");  
                } catch (InterruptedException e) {  
                    e.printStackTrace();  
                }finally {
                    exec.shutdown();
                }
            }
        };
        exec.execute(game);
        for (int i = 0; i < 10; i++) {
            exec.execute(new Player("玩家小"+i,count));  
        }
  
    }
}
class Player implements Runnable{
    private String loler;
    private CountDownLatch count;
    public Player(String loler,CountDownLatch count){  
        this.loler = loler;
        this.count = count;
    }
    public String getLoler() {
        return loler;
    }
    public void setLoler(String loler) { 
        this.loler = loler;
    }
    @Override
  public void run() {
        //加载完成一个玩家,则计数器减一
  count.countDown();
        System.out.println(this.loler+"加载lol完毕!");
    }
}

结果如下~

重要方法

  • 构造函数 传入count数量
public CountDownLatch(int count) {  
    if (count < 0) throw new IllegalArgumentException("count < 0");  
    this.sync = new Sync(count);  
}
  • await()方法,等待CountDownLatch 内部维护的count为0时执行余下逻辑
  • countDown() 方法,将CountDownLatch内部的count数量-1
  • 内部类重写了tryAcquireShared方法来判断是否释放当前线程
protected int tryAcquireShared(int acquires) {  
    return (getState() == 0) ? 1 : -1;  
}  
  
protected boolean tryReleaseShared(int releases) {  
    // Decrement count; signal when transition to zero  
  for (;;) {  
        int c = getState();  
        if (c == 0)  
            return false;  
        int nextc = c-1;  
        if (compareAndSetState(c, nextc))  
            return nextc == 0;  
    }

重要变量

  • 静态内部类 private static final class Sync extends AbstractQueuedSynchronizer
  • Sync继承自 的AbstractQueuedSynchronizer的变量 private volatile int state;

原理

  • 构造函数将count 传入静态内部类Sync的state,根据监控state是否为0来阻塞await的线程

Cyclicbarrier

循环栅栏,

构造函数

也是用到了方法的重载

public CyclicBarrier(int parties, Runnable barrierAction) {  
    if (parties <= 0) throw new IllegalArgumentException();  
    this.parties = parties;  
    this.count = parties;  
    this.barrierCommand = barrierAction;  
}

public CyclicBarrier(int parties) {  
    this(parties, null);  
}

重要方法

  • await 没错还是他,阻塞线程,等待所有 parties个线程都ok了才往下走

举个例子

线程await后互相等待

package com.juc;  
  
import java.util.concurrent.BrokenBarrierException;  
import java.util.concurrent.CyclicBarrier;  
import java.util.concurrent.ExecutorService;  
import java.util.concurrent.LinkedBlockingQueue;  
import java.util.concurrent.ThreadPoolExecutor;  
import java.util.concurrent.TimeUnit;  
  
public class CyclicBarrierTest {  
    public static void main(String\[\] args) {  
        //主线程 加载游戏资源  
  CyclicBarrier cyclicBarrier = new CyclicBarrier(10);  
        //十个子线程代表十个玩家正在加载游戏  
  ExecutorService exec = new ThreadPoolExecutor(10, 10,  
                0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());  
        for (int i = 0; i < 10; i++) {  
            exec.execute(new Player1("玩家小" \+ i, cyclicBarrier));  
        }  
    }  
}  
  
 class Player1 implements Runnable {  
    private String loler;  
  
    private CyclicBarrier cyclicBarrier;  
  
    public Player1(String loler, CyclicBarrier cyclicBarrier) {  
        this.loler = loler;  
        this.cyclicBarrier = cyclicBarrier;  
    }  
  
    public String getLoler() {  
        return loler;  
    }  
  
    public void setLoler(String loler) {  
        this.loler = loler;  
    }  
  
    @Override  
  public void run() {  
        //加载完成一个玩家,则计数器减一  
  System.out.println(this.loler \+ "开始加载游戏!");  
        try {  
            cyclicBarrier.await();  
        } catch (InterruptedException e) {  
            e.printStackTrace();  
        } catch (BrokenBarrierException e) {  
            e.printStackTrace();  
        }  
        System.out.println(this.loler \+ "加载lol完毕!");  
    }  
}

结果就是这样

// 构造函数传入 一个Runnble的实现类,瞅瞅他会咋样
package com.juc;  
  
import java.util.concurrent.BrokenBarrierException;  
import java.util.concurrent.CyclicBarrier;  
import java.util.concurrent.ExecutorService;  
import java.util.concurrent.LinkedBlockingQueue;  
import java.util.concurrent.ThreadPoolExecutor;  
import java.util.concurrent.TimeUnit;  
  
public class CyclicBarrierTest {  
    public static void main(String[] args) {  
  
  CyclicBarrier cyclicBarrier1 = new CyclicBarrier(10, new Thread(()->{  
            System.out.println("大家都加载了啊,那就开始进入游戏把");  
        }));  
        //十个子线程代表十个玩家正在加载游戏  
  ExecutorService exec1 = new ThreadPoolExecutor(10, 10,  
                0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());  
        for (int i = 0; i < 10; i++) {  
            exec1.execute(new Player1("玩家小" \+ i, cyclicBarrier1));  
        }  
    }  
}  
  
 class Player1 implements Runnable {  
    private String loler;  
  
    private CyclicBarrier cyclicBarrier;  
  
    public Player1(String loler, CyclicBarrier cyclicBarrier) {  
        this.loler = loler;  
        this.cyclicBarrier = cyclicBarrier;  
    }  
  
    public String getLoler() {  
        return loler;  
    }  
  
    public void setLoler(String loler) {  
        this.loler = loler;  
    }  
  
    @Override  
  public void run() {  
        //加载完成一个玩家,则计数器减一  
  System.out.println(this.loler \+ "开始加载游戏!");  
        try {  
            cyclicBarrier.await();  
        } catch (InterruptedException e) {  
            e.printStackTrace();  
        } catch (BrokenBarrierException e) {  
            e.printStackTrace();  
        }  
//        System.out.println(this.loler + "加载lol完毕!");  
  System.out.println(this.loler  + "进入游戏!");  
    }  
}

从结果可以看出,所有线程都await()准备就绪后,开始跑传入构造函数的线程。再开始所有线程await后的逻辑。

有reset方法,让内部的count重置,重新开始,可以复用Cyclicbarrier对象,对比DownCountLatch。如果DownCountLatch在执行countDown()后,调用await也可以达到Cyclicbarrier的await方法等待其余剩余线程的feel

reset方法使用案例

package com.juc;  
  
import java.util.concurrent.BrokenBarrierException;  
import java.util.concurrent.CyclicBarrier;  
import java.util.concurrent.ExecutorService;  
import java.util.concurrent.LinkedBlockingQueue;  
import java.util.concurrent.ThreadPoolExecutor;  
import java.util.concurrent.TimeUnit;  
  
public class CyclicBarrierTest {  
    public static void main(String\[\] args) {  
        CyclicBarrier cyclicBarrier = new CyclicBarrier(10, new Thread(() -> {  
            System.out.println("大家都加载了啊,那就开始进入游戏把");  
        }));  
        //十个子线程代表十个玩家正在加载游戏  
  ExecutorService exec1 = new ThreadPoolExecutor(10, 10,  
                0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());  
        for (int i = 0; i < 10; i++) {  
            exec1.execute(new Player1("玩家小" \+ i, cyclicBarrier));  
        }  
        exec1.shutdown();  
        cyclicBarrier.reset();  
        ExecutorService exec = new ThreadPoolExecutor(10, 10,  
                0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());  
        for (int i = 0; i < 10; i++) {  
            exec.execute(new Player1("第二次玩家小" \+ i, cyclicBarrier));  
        }  
  
    }  
}  
  
class Player1 implements Runnable {  
    private String loler;  
  
    private CyclicBarrier cyclicBarrier;  
  
    public Player1(String loler, CyclicBarrier cyclicBarrier) {  
        this.loler = loler;  
        this.cyclicBarrier = cyclicBarrier;  
    }  
  
    public String getLoler() {  
        return loler;  
    }  
  
    public void setLoler(String loler) {  
        this.loler = loler;  
    }  
  
    @Override  
  public void run() {  
        //加载完成一个玩家,则计数器减一  
  System.out.println(this.loler \+ "开始加载游戏!");  
        try {  
            cyclicBarrier.await();  
        } catch (InterruptedException e) {  
            e.printStackTrace();  
        } catch (BrokenBarrierException e) {  
            e.printStackTrace();  
        }  
        if ("玩家小5".equals(loler)) {  
            cyclicBarrier.reset();  
        }  
        System.out.println(this.loler \+ "进入游戏!");  
    }  
}

Semaphore

用处

只有在调用Semaphore对象的aquire()成功后才可以往下执行,完成后执行release()释放持有的信号量,下一个线程就可以马上获取这个空闲信号量进入执行。

白话文:有两个茅坑,有五个人,五个人是线程,要占用两个茅坑 方便,当茅坑被线程占用aquire时,其他线程只能等着,一个结束了,另一个再上。

构造函数

// 传入可持有的信号量,同一时刻,只有permits个线程能跑
public Semaphore(int permits){  
    sync = new NonfairSync(permits);  
}
public Semaphore(int permits, boolean fair) {  
    sync = fair ? new FairSync(permits) : new NonfairSync(permits);  
}

重要方法

// 获取信号量
public void acquire() throws InterruptedException {  
    sync.acquireSharedInterruptibly(1);  
}
// 释放获取的信号量
public void release() {  
    sync.releaseShared(1);  
}

DEMO

package com.juc;  
  
import java.util.concurrent.Semaphore;  
  
public class SemaphoreTest {  
  
    static class MyThread extends Thread{  
        private String name1;  
        private Semaphore semaphore;  
  
        public String getName1() {  
            return name1;  
        }  
  
        public void setName1(String name1) {  
            this.name1 = name1;  
        }  
  
        public Semaphore getSemaphore() {  
            return semaphore;  
        }  
  
        public void setSemaphore(Semaphore semaphore) {  
            this.semaphore = semaphore;  
        }  
  
        public MyThread(String name1, Semaphore semaphore) {  
            this.name1 = name1;  
            this.semaphore = semaphore;  
        }  
  
        @Override  
  public void run() {  
            try {  
                semaphore.acquire();  
                System.out.println(this.name1 +";acquire占坑拉屎");  
                Thread.sleep(2000);  
            } catch (InterruptedException e) {  
                e.printStackTrace();  
            }  
            semaphore.release();  
            System.out.println(this.name1 +";release拉完走人");  
        }  
    }  
  
    public static void main(String[] args) {  
        Semaphore semaphore = new Semaphore(2);  
        for (int i = 0; i < 4; i++) {  
            new MyThread(""+i,semaphore).start();  
        }  
  
    }  
}

那么结论很明显,同一时间只有两个线程在跑,其他线程要运行必须等其中一个线程release完毕才可

总结

无论从性能还是安全上考虑都是JUC并发包的信号同步类比较好,避免使用对象的wait(),notify()方法

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!