一直搞不懂Java线程通信,这次终于明白了( 六 )

  • 调用signalAll():将等待队列中的所有节点全部唤醒,相当于将等待队列中的每一个节点都执行一次signal()

  • 一直搞不懂Java线程通信,这次终于明白了

    文章插图
    CountDownLatchJava5之后在 java.util.concurrent 也就是【JUC】包中提供了很多并发编程的工具类,如 CountDownLatch 计数器是基于 AQS 框架实现的多个线程之间维护共享变量的类
    使用场景可以通过 CountDownLatch 使当前线程阻塞,等待其他线程完成给定任务,比如,等待线程完成下载任务之后,提示用户下载完成;导游等待所有游客参观完之后去下一个景点等
    使用介绍CountDownLatch的构造函数接收一个int类型的参数作为计数器,如果你想等待n个点完成,这里就传入n 。这里所说的n个点,可以是n个线程,也可以是1个线程里的n个执行步骤 。CountDownLatch 构造函数如下:
    public CountDownLatch(int count) {if (count < 0) throw new IllegalArgumentException("count < 0");this.sync = new Sync(count); }
    计数器参数count必须大于等于0,等于0的时候,调用await方法时不会阻塞当前线程 。
    当我们调用CountDownLatch的countDown()方法时,n就会减1,CountDownLatch的await()方法会阻塞当前线程,直到n变成零,继续执行 。
    CountDownLatch 方法
    • await():阻塞当前线程,直到计数器为零为止
    • await(long timeout, TimeUnit unit):await()的重载方法,可以指定阻塞时长
    • countDown():计数器减1,如果计数达到零,释放所有等待的线程
    • getCount(): 返回当前计数
    案例:比如开一把英雄联盟,需要10个人加载完成才会进入游戏,可以理解为10个线程运行完毕之后进入游戏页面
    import java.util.ArrayList;import java.util.Arrays;import java.util.List;import java.util.Random;import java.util.concurrent.CountDownLatch;public class LoadingGame {public static void main(String[] args) {// 计数器CountDownLatch latch = new CountDownLatch(10);// 玩家数组String[] player = new String[10];// 随机数,用来加载进度条时线程睡眠使用,防止直接加载到100Random random = new Random();// 循环开启10个线程,即10个玩家for (int i = 0; i < 10; i++) {// 记录玩家在数组中的下标int index = i;new Thread(() -> {// 循环进度条到100for (int j = 0; j <= 100; j++) {try {// 每加载 1% 就随机睡眠一段时间Thread.sleep(random.nextInt(100));} catch (InterruptedException e) {throw new RuntimeException(e);}// 修改指定玩家进度条player[index] = j +"%";// 输出当前所有的玩家进度System.out.print("r" + Arrays.toString(player));}// 每加载完一个玩家计数-1latch.countDown();}).start();}try {// 阻塞当前线程【main线程】,等待十个玩家加载结束后唤醒latch.await();} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("n"+"游戏开始");}}运行结果:发现主线程等待10个子线程加载到100时才执行
    一直搞不懂Java线程通信,这次终于明白了

    文章插图
    高频面试题——如何保证多个线程按顺序执行其实就是让线程按照指定的顺序一个一个执行,这里结合同一案例给大家介绍4种方法:
    案例:老师布置作业之后,学生开始写作业,学生写完作业老师批改,之后老师再将学生的作业情况记录下来,这个顺序不可错乱
    Thread的join方法public class HomeworkJoin {public static void main(String[] args) {// 布置作业线程Thread t1 = new Thread(() -> {System.out.println("......老师布置作业......");});// 学生写作业,需要等待老师布置完Thread t2 = new Thread(() -> {try {// t1插入执行,也就是插队t1.join();} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("......学生写作业......");});// 学生写作业,需要等待老师布置完Thread t3 = new Thread(() -> {try {// t2插队t2.join();} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("......老师检查作业......");});// 学生写作业,需要等待老师布置完Thread t4 = new Thread(() -> {try {// t3插队t3.join();} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("......老师记录作业情况......");});// 开启线程t1.start();t2.start();t3.start();t4.start();// t1线程插队try {t4.join();} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("......作业布置和检查结束......");}}


    推荐阅读