一篇文章让你了解JUC的辅助类

84 阅读3分钟

JUC辅助类

1.CountDownLatch

CountDownLatch 允许一个或多个线程等待一些特定的操作完成,而这些操作是在其它的线程中进行的,也就是说会出现 等待的线程被等的线程 这样分明的角色;

CountDownLatch 构造函数中有一个 count 参数,表示有多少个线程需要被等待,对这个变量的修改是在其它线程中调用 countDown 方法,每一个不同的线程调用一次 countDown 方法就表示有一个被等待的线程到达,count 变为 0 时,latch(门闩)就会被打开,处于等待状态的那些线程接着可以执行;

CountDownLatch 是一次性使用的,也就是说latch门闩只能只用一次,一旦latch门闩被打开就不能再次关闭,将会一直保持打开状态,因此 CountDownLatch 类也没有为 count 变量提供 set 的方法;

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()+"号同学离开了教室");
                //计数减一
                countDownLatch.countDown();
            },String.valueOf(i)).start();
        }
        //等带只有当计数减为0的时候才会执行后面的代码
        countDownLatch.await();
        System.out.println(Thread.currentThread().getName()+"班长关门");
    }
}

2.CyclicBarrier

CyclicBarrier也叫同步屏障,在JDK1.5被引入,可以让一组线程达到一个屏障时被阻塞,直到最后一个线程达到屏障时,所以被阻塞的线程才能继续执行。 CyclicBarrier好比一扇门,默认情况下关闭状态,堵住了线程执行的道路,直到所有线程都就位,门才打开,让所有线程一起通过。

public class CyclicBarrierDemo {
    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier =new CyclicBarrier(7,()->{
            System.out.println("所有运动员到了,准备起跑");//当阻塞被释放执行
        });
        for (int i=1;i<=7;i++){
            new Thread(()->{
                System.out.println("第"+Thread.currentThread().getName()+"位运动员到场");
                try {
                    cyclicBarrier.await();
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            },String.valueOf(i)).start();
​
        }
    }
}

每个线程调用await方法告诉CyclicBarrier已经到达屏障位置,线程被阻塞,每阻塞一次CyclicBarrier内部的计数器就会加一,当计数器的值和自己创建时设置的值一样时,就会将阻塞的线程全部一起放出去然后执行()->{System.out.println("所有运动员到了,准备起跑"); }

3.Semaphore

一个计数信号量,从概念上将,信号量维护了一个许可集,如有必要,在许可可用前会阻塞每一个acquire(),然后在获取该许可。每个release()添加一个许可,从而可能释放一个正在阻塞的获取者。但是,不使用实际的许可对象,Semaphore只对可用许可的号码进行计数,并采取相应的行动

Semaphore(int permits) 创建具有给定的许可数和非公平的公平设置的Semapore,当permits为1时就和可重入锁一样

public Semaphore(int permits) {
    // 默认创建非公平锁
    sync = new NonfairSync(permits);
}
public Semaphore(int permits, boolean fair) {
    // fair为true时,为公平锁
    sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}

acquire()从此信号量获取一个许可,在提供一个许可前一直将线程阻塞,否则线程被中断

release()释放一个许可,将其返回给信号量

public void acquire() throws InterruptedException {  //相当于上锁
    // 阻塞当前线程
    sync.acquireSharedInterruptibly(1);
}
public void release() { //相当于解锁
    // 释放一个许可
    sync.releaseShared(1);
}

设置许可数量 Semaphore semaphore = new Semaphore(3);

一般 acquire()都会抛出异常,release 在 finally 中执行

通过具体案例 6辆汽车,停3个车位

示例代码如下

public class SemaphoreTest {
    public static void main(String[] args) {
        //创建Semaphore,设置许可数量
        Semaphore semaphore = new Semaphore(3);
        for (int i = 1; i <= 6; i++) {
            new Thread(()->{
                try {
                    // 抢占
                    semaphore.acquire();//上锁
                    System.out.println(Thread.currentThread().getName()+"抢到了车位");
                    // 设置停车时间
                    TimeUnit.SECONDS.sleep(new Random().nextInt(5));
                    // 离开车位
                    System.out.println(Thread.currentThread().getName()+"------离开了车位");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    //释放
                    semaphore.release();//解锁
                }
            },String.valueOf(i)).start();
        }
    }
}
​

输出结果如下:

2抢到了车位 1抢到了车位 3抢到了车位 2------离开了车位 4抢到了车位 4------离开了车位 5抢到了车位 3------离开了车位 6抢到了车位 1------离开了车位 5------离开了车位 6------离开了车位