【JUC并发编程08】JUC强大辅助类

8 JUC强大辅助类

该辅助类主要讲述三个

  1. 减少计数 CountDownLatch

  2. 循环栅栏 CyclicBarrier

  3. 信号灯 Semaphore

8.1 减少计数CountDownLatch

该类的构造方法为 CountDownLatch(int count) 构造一个用给定计数初始化的 CountDownLatch 在这里插入代码片

public CountDownLatch(int count) {
    if (count < 0) throw new IllegalArgumentException("count < 0");
    this.sync = new Sync(count);
}

CountDownLatch 类可以设置一个计数器,然后通过 countDown 方法来进行减 1 的操作,使用 await 方法等待计数器不大于 0,然后继续执行 await 方法之后的语句。具体步骤可以演化为定义一个类,减1操作,并等待到0,为0执行结果

两个常用的主要方法
await() 使当前线程在锁存器倒计数至零之前一直在等待,除非线程被中断

public void await() throws InterruptedException {
    sync.acquireSharedInterruptibly(1);
}

countDown()递减锁存器的计数,如果计数达到零,将释放所有等待的线程

public void countDown() {
    sync.releaseShared(1);
}
public final boolean releaseShared(int arg) {
    if (tryReleaseShared(arg)) {
        doReleaseShared();
        return true;
    }
    return false;
}

通过具体的案例进行加深代码
6个同学陆续离开教室之后,班长才能锁门
如果不加 CountDownLatch类,会出现线程混乱执行,同学还未离开教室班长就已经锁门了

在不使用CountDownLatch的时候

public class CountDownLatchTest {
    //6个同学陆续离开教室之后,班长锁门
    public static void main(String[] args) throws InterruptedException {
        // 创建六个线程,模拟六个学生
        for (int i = 1; i <=6; i++) {
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+"离开教室");
            },String.valueOf(i)).start();
        }
        System.out.println(Thread.currentThread().getName()+"锁门");
    }
}

输出结果为:

2离开教室
6离开教室
5离开教室
4离开教室
1离开教室
main锁门
3离开教室

这就造成了,线程的混乱问题 ,可以通过CountDownLatch计数,直到为0 ,才结束

public class CountDownLatchTest {
    public static void main(String[] args) throws InterruptedException {
        // 创建CountDown对象并设置初始值
        CountDownLatch countDownLatch = new CountDownLatch(6);
        // 创建六个线程,模拟六个学生
        for (int i = 1; i <= 6; i++) {
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+"离开教室");
                // 计数 -1
                countDownLatch.countDown();
            },String.valueOf(i)).start();
        }
        // 等待,直到达到零
        countDownLatch.await();
        System.out.println(Thread.currentThread().getName()+"锁门");
    }
}

8.2 循环栅栏 CyclicBarrier

该类是 允许一组线程 互相 等待,直到到达某个公共屏障点,在设计一组固定大小的线程的程序中,这些线程必须互相等待,因为barrier在释放等待线程后可以重用,所以称为循环barrier

常用的构造方法有:CyclicBarrier(int parties,Runnable barrierAction),其底层代码如下

public CyclicBarrier(int parties, Runnable barrierAction) {
    if (parties <= 0) throw new IllegalArgumentException();
    this.parties = parties;
    this.count = parties;
    // 最后需要执行的方法
    this.barrierCommand = barrierAction;
}

创建一个新的CyclicBarrier,它将在给定数量的参与者(线程)处于等待状态时启动,并在启动barrier时执行给定的屏障操作,该操作由最后一个进入barrier的线程操作

常用的方法有:

await() 在所有的参与者都已经在此barrier上调用await方法之前一直等待。

public int await() throws InterruptedException, BrokenBarrierException {
    try {
        return dowait(false, 0L);
    } catch (TimeoutException toe) {
        throw new Error(toe); // cannot happen
    }
}
private int dowait(boolean timed, long nanos)
    throws InterruptedException, BrokenBarrierException,
TimeoutException {
    final ReentrantLock lock = this.lock;
    // 同步锁
    lock.lock();
    try {
        final Generation g = generation;

        if (g.broken)
            throw new BrokenBarrierException();

        if (Thread.interrupted()) {
            breakBarrier();
            throw new InterruptedException();
        }
		// 每次执行 CyclicBarrier 一次障碍数会加一,距离目标障碍数-1
        int index = --count;
        // 当达到目标障碍数执行if内代码
        if (index == 0) {  // tripped
            boolean ranAction = false;
            try {
                final Runnable command = barrierCommand;
                if (command != null)
                    // 执行Runnable代码
                    command.run();
                ranAction = true;
                nextGeneration();
                // 返回0
                return 0;
            } finally {
                if (!ranAction)
                    breakBarrier();
            }
        }

        // 循环,直到触发、中断、中断或超时  
        for (;;) {
            try {
                if (!timed)
                    trip.await();
                else if (nanos > 0L)
                    nanos = trip.awaitNanos(nanos);
            } catch (InterruptedException ie) {
                if (g == generation && ! g.broken) {
                    breakBarrier();
                    throw ie;
                } else {
                    // We're about to finish waiting even if we had not
                    // been interrupted, so this interrupt is deemed to
                    // "belong" to subsequent execution.
                    Thread.currentThread().interrupt();
                }
            }

            if (g.broken)
                throw new BrokenBarrierException();

            if (g != generation)
                return index;

            if (timed && nanos <= 0L) {
                breakBarrier();
                throw new TimeoutException();
            }
        }
    } finally {
        lock.unlock();
    }
}

从底层代码可知, CyclicBarrier 的构造方法第一个参数是目标障碍数,每次执行 CyclicBarrier 一次障碍数会+1,如果达到了目标障碍数,才会执行 cyclicBarrier.await()之后的语句。可以将 CyclicBarrier 理解为+1 操作(指与目标障碍数的距离)

通过具体案例
集齐7颗龙珠就可以召唤神龙

public class CyclicBarrirtTest {
    // 创建固定值
    private static final int NUMBER  = 7;
    public static void main(String[] args) {
        // 每次执行 CyclicBarrier 一次障碍数会加一,如果达到了目标障碍数,才会执行 cyclicBarrier.await()之后的语句。
        CyclicBarrier cyclicBarrier = new CyclicBarrier(NUMBER, () -> {
            System.out.println("****集齐7颗龙珠就可以召唤神龙");
        });
        // 创建六个线程,模拟六个学生
        for (int i = 1; i <= 7; i++) {
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+" 星龙被收集到了");
                try {
                    // 计数 +1
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }

            },String.valueOf(i)).start();
        }
    }
}

输出结果为:

3 星龙被收集到了
5 星龙被收集到了
4 星龙被收集到了
2 星龙被收集到了
1 星龙被收集到了
6 星龙被收集到了
7 星龙被收集到了
****集齐7颗龙珠就可以召唤神龙

8.3 信号灯 Semaphore

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

具体常用的构造方法有:

Semaphore(int permits) 创建具有给定的许可数和非公平的公平设置的Semapore

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------离开了车位

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_之桐_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值