7、常用的辅助类(必会)

CountDownLatch 减法计数器

官方文档介绍

使用

  • 举例:教室里有6个人,门卫需要等到人走完了才会去关门

  • 开启6个线程作为人,计数器判断线程执行完毕后,才会执行接下去的代码。

package com.zxh.add;

import java.util.concurrent.CountDownLatch;

public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {
        // 计数器,设置总数为6
        CountDownLatch countDownLatch = new CountDownLatch(6);
// 教室里有6个人
        for (int i = 1; i <= 6; i++) {
            new Thread(()->{
                System.out.println(Thread.currentThread().getName() + "离开了教室");
                countDownLatch.countDown(); // 总数 -1
            }, String.valueOf(i)).start();
        }

        countDownLatch.await(); //等待总数变为0,才会被唤醒,接下去执行
        System.out.println("门卫关门");
    }
}

  • 如果没有countDownLatch.await(),计数器不会等待,执行顺序就会乱

原理

countDownLatch.countDown(); // 数量-1
countDownLatch.await(); // 等待计数器归零,然后再向下执行。
每次有线程调用 countDown() 数量-1,假设计数器变为0,countDownLatch.await() 就会被唤醒,继续
执行!

CyclicBarrier加法计数器

官方文档

 构造方法

使用

  • 举例:集齐7颗龙珠

  • 就是有7个线程,每个线程执行完毕后,都等待其他线程达到屏障,就是调用一下await()方法

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierDemo {
    public static void main(String[] args) {
        // 等待7个线程都操作了这个await方法到达屏障后,就会执行另一个线程
        CyclicBarrier cyclicBarrier = new CyclicBarrier(7, ()->{
            System.out.println("集齐7颗龙珠,召唤神龙成功!");
        });

        for (int i = 1; i <= 7; i++) {
            final int temp = i;
            new Thread(()->{
                System.out.println(Thread.currentThread().getName() + "集齐了" + temp + "颗龙珠");
                try {
                    cyclicBarrier.await();  // 该线程调用await(),到达屏障
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

注意点

final int temp = i;

线程操作的变量为什么要变成一个常量?

  • 外部变量可能会在线程还没有执行完毕时,就销毁了。

  • 那么就给创建一个新的变量temp等于外部变量,获取到外部变量的值,并且添加final修饰符,就会将值存储在常量池中,temp变量指向它。

  • 匿名内部类使用的是temp变量,值和外部变量相同。

  • 当外部变量销毁时,也就是生命周期结束后,这个temp还是会指向内存中的常量池内保存的值。

  • 那么线程就可以依旧访问的到值。

Semaphore信号量

官方文档

 构造方法,其中的许可证指的是信号量

 使用方法,在文档中已经给出了,下面做了一个举例

 常用方法:

使用

  • 举例:抢车位,6个车子抢3个车位

  • 这里把信号量比作车位,6个车子比作线程

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

public class SemaphoreDemo {
    public static void main(String[] args) {
        // 设置最大的信号量为3个!用于限流!
        Semaphore semaphore = new Semaphore(3);

        // acquire()获得资源
        // relase()释放资源
        for (int i = 1; i <= 6; i++) {
            new Thread(()->{
                try {
                    semaphore.acquire();    // 获取信号量,也就是对信号量的 -1 操作,如果没有信号量,则等待
                    System.out.println(Thread.currentThread().getName() + "抢到车位");
                    TimeUnit.SECONDS.sleep(2);  // 模拟车子停了一段时间
                    System.out.println(Thread.currentThread().getName() + "离开了车位");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    semaphore.release();    // 释放资源,返回信号量,也就是 +1 操作,唤醒等待的线程
                }
            }, String.valueOf(i)).start();
        }
    }
}

原理

资源 相当于 信号量

semaphero.acquire()获取资源,假如资源已经分配完了,也就是说车位满了,就会等待,等待资源被释放!

semaphero.release():释放资源,会将当前的信号量释放,就是 +1 操作,然后唤醒等待的线程!

相当于线程中的通信,只不过封装成了一个工具类

作用:多个共享资源互斥的使用!并发限流,控制最大的线程数!保障系统的安全

posted @ 2020-05-25 15:51  忘忧山的兰木  阅读(210)  评论(0编辑  收藏  举报
她只是想吃这个而已啊……这一定是她非常爱吃的,我居然连如此细微的幸福也夺走了……
Hide
Switch
Save