前言:

项目中我们经常会遇到有时候需要等待其他线程完成任务后,主线程才能执行其他任务,那么我们将如何实现呢?

Join 解决方案

join 的工作原理是,检查thread是否存活,如果存活则让当前线程永远wait,直到 thread线程终止,线程的 notifyAll才会被调用。

具体实现

public class JoinAThread extends Thread
{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()   
                " 线程开始");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
         System.out.println( Thread.currentThread().getName()   
                " 线程执行完毕");
    }
}

public class JoinBThread extends Thread
{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()   
                " 线程开始");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
         System.out.println( Thread.currentThread().getName()   
                " 线程执行完毕");
    }
}

public class JoinTest
{
   public static void main(String[] args) throws InterruptedException
{
       JoinAThread joinA =new JoinAThread();
       Thread threadA =new Thread(joinA,"线程A");

       JoinBThread joinB =new JoinBThread();
       Thread threadB =new Thread(joinB,"线程B");
       threadA.start();
       threadB.start();
       threadA.join();
       threadB.join();

       System.out.println("子线程执行完成了,主线程" Thread.currentThread().getName() "开始执行了");
     }
}

执行结果

从结果中,我们可以看出只有子线程执行完成了,主线程才开始执行。join的实现我们需要每个线程进行join,如果存在多个线程,那么写起来会比较的繁琐,那么又没更新优化的方案了,答案是JUC下面的工具类CountDownLatch,也能完成同样的功能。

CountDownLatch 解决方案

具体实现

public class CountDownLatchTest
{
    private static Logger logger =LoggerFactory.getLogger(CountDownLatchTest.class);
    public static void main(String[] args) throws InterruptedException
    {
        ExecutorService exec = Executors.newCachedThreadPool();
        final CountDownLatch countDownLatch = new CountDownLatch(10);
        for (int i = 1; i <= 10; i  ){
            exec.execute(() -> {
                try {
                    invokeServiec();
                } catch (InterruptedException e) 
                {
                    logger.info("invoce service error",e);
                }
                finally 
                {
                    //计数器减一
                    countDownLatch.countDown();
                }
            });
        }
        countDownLatch.await();
        logger.info("所有的子线程执行完成,主线程" Thread.currentThread().getName() "开始执行");
    }

    private static void invokeServiec() throws InterruptedException
    {
        logger.info(Thread.currentThread().getName() ",开始执行任务");
        Thread.sleep(300);
    }
}

说明:CountDownLatch中有两个方法一个是await()方法,调用这个方法的线程会被阻塞,另外一个是countDown() 方法,调用此方法会使计数器减一,当计数器的值为0时,调用await()方法被阻塞的线程才会被唤醒。

执行结果:

原理说明

CountDownLatch 是一个计数器闭锁,通过它可以完成类似于阻塞当前线程的功能,即:一个线程或多个线程一直等待,直到其他线程执行的操作完成。

基本原理

CountDownLatch

CountDownLatch内部定义计数器和一个队列。当计数器的值递减为0之前,阻塞队列里面的线程处于挂起状态,当计数器递减到0时会唤醒阻塞队列所有线程,计数器是一个标志,可以表示一个任务一个线程,也可以表示一个倒计时器。

常用的方法

countDown:用于使计数器减一,其一般是执行任务的线程调用. await: 使用线程处于等待状态,其一般是主线程调用.

countDown

countDown实现方法如下:

说明:sync是一个AQS的队列,调用的为AQS的releaseShared方法,其具体实现如下:

而releaseShared调用为CountDownLatch中的内部类sync中的tryReleaseShared方法,具体实现如下:

tryReleaseShared(int)方法即对state属性进行减一操作的代码.通过CAS进行减操作来保证原子性,其会比较state是否为c,如果是则将其设置为nextc(自减1),如果state不为c,则说明有另外的线程在getState()方法和compareAndSetState()方法调用之间对state进行了设置,当前线程也就没有成功设置state属性的值,其会进入下一次循环中,如此往复,直至其成功设置state属性的值,即countDown()方法调用成功。

而doReleaseShared方法调用的为AbstractQueuedSynchronizer简称AQS的doReleaseShared方法,

说明:首先判断头结点不为空,且不为尾节点,说明等待队列中有等待唤醒的线程,这里需要说明的是,在等待队列中,头节点中并没有保存正在等待的线程,其只是一个空的Node对象,真正等待的线程是从头节点的下一个节点开始存放的,因而会有对头结点是否等于尾节点的判断。在判断等待队列中有正在等待的线程之后,其会清除头结点的状态信息,并且调用unparkSuccessor(Node)方法唤醒头结点的下一个节点,使其继续往下执行。如下是unparkSuccessor(Node)方法的具体实现:

可以看到,unparkSuccessor(Node)方法的作用是唤醒离传入节点最近的一个处于等待状态的线程,使其继续往下执行。

await

await方法实现如下:

await()方法调用了Sync对象的方法acquireSharedInterruptibly(int)方法,该方法的具体实现如下:

g

在doAcquireSharedInterruptibly(int)方法中,首先使用当前线程创建一个共享模式的节点。然后在一个for循环中判断当前线程是否获取到执行权限,如果有(r >= 0判断)则将当前节点设置为头节点,并且唤醒后续处于共享模式的节点;如果没有,则对调用shouldParkAfterFailedAcquire(Node, Node)和parkAndCheckInterrupt()方法使当前线程处于"搁置"状态,该"搁置"状态是由操作系统进行的,这样可以避免该线程无限循环而获取不到执行权限,造成资源浪费,这里也就是线程处于等待状态的位置,也就是说当线程被阻塞的时候就是阻塞在这个位置。当有多个线程调用await()方法而进入等待状态时,这几个线程都将等待在此处。

总结

本文对JUC的工具类CountDownLatch进行详细的讲解,如有疑问请随时反馈。

到此这篇关于JUC系列学习工具类CountDownLatch详解的文章就介绍到这了,更多相关JUC工具类CountDownLatch 内容请搜索Devmax以前的文章或继续浏览下面的相关文章希望大家以后多多支持Devmax!

JUC系列学习工具类CountDownLatch详解的更多相关文章

  1. Java日期工具类的封装详解

    在日常的开发中,我们难免会对日期格式化,对日期进行计算,对日期进行校验,为了避免重复写这些琐碎的逻辑,我这里封装了一个日期工具类,方便以后使用,直接复制代码到项目中即可使用,需要的可以参考一下

  2. Java中RedisUtils工具类的使用

    本文主要介绍了Java中RedisUtils工具类的使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  3. Java多线程同步工具类CountDownLatch详解

    这篇文章主要介绍了Java多线程同步工具类CountDownLatch详解,CountDownLatch是一个多线程同步工具类,在多线程环境中它允许多个线程处于等待状态,直到前面的线程执行结束

  4. Python常用工具类之adbtool示例代码

    本文主要介绍了Python中常用工具类之adb命令的使用,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  5. Android读写文件工具类详解

    这篇文章主要为大家详细介绍了Android读写文件工具类,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  6. PHP实现可精确验证身份证号码的工具类示例

    这篇文章主要介绍了PHP实现可精确验证身份证号码的工具类,涉及php针对字符串的截取、计算、判断等相关操作技巧,并附带说明了身份证号码的原理,需要的朋友可以参考下

  7. java并发包工具CountDownLatch源码分析

    这篇文章主要为大家介绍了java并发包工具CountDownLatch源码分析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  8. Java并发工具类Future使用示例

    这篇文章主要介绍了Java并发工具类Future使用示例,本文需要注意future.get()方法是阻塞式的,如果调用该方法的时候任务尚未执行完成,则会一直等待下去,直到任务执行结束,本文通过示例代码给大家介绍的非常详细,需要的朋友参考下吧

  9. 工具类之关于RestTemplateUtil工具类的使用

    这篇文章主要介绍了工具类之关于RestTemplateUtil工具类的使用,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  10. PHP常用的类封装小结【4个工具类】

    这篇文章主要介绍了PHP常用的类封装,结合实例形式分析了4个常用的工具类,包括Mysql类、分页类、缩略图类、上传类,需要的朋友可以参考下

随机推荐

  1. 基于EJB技术的商务预订系统的开发

    用EJB结构开发的应用程序是可伸缩的、事务型的、多用户安全的。总的来说,EJB是一个组件事务监控的标准服务器端的组件模型。基于EJB技术的系统结构模型EJB结构是一个服务端组件结构,是一个层次性结构,其结构模型如图1所示。图2:商务预订系统的构架EntityBean是为了现实世界的对象建造的模型,这些对象通常是数据库的一些持久记录。

  2. Java利用POI实现导入导出Excel表格

    这篇文章主要为大家详细介绍了Java利用POI实现导入导出Excel表格,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  3. Mybatis分页插件PageHelper手写实现示例

    这篇文章主要为大家介绍了Mybatis分页插件PageHelper手写实现示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  4. (jsp/html)网页上嵌入播放器(常用播放器代码整理)

    网页上嵌入播放器,只要在HTML上添加以上代码就OK了,下面整理了一些常用的播放器代码,总有一款适合你,感兴趣的朋友可以参考下哈,希望对你有所帮助

  5. Java 阻塞队列BlockingQueue详解

    本文详细介绍了BlockingQueue家庭中的所有成员,包括他们各自的功能以及常见使用场景,通过实例代码介绍了Java 阻塞队列BlockingQueue的相关知识,需要的朋友可以参考下

  6. Java异常Exception详细讲解

    异常就是不正常,比如当我们身体出现了异常我们会根据身体情况选择喝开水、吃药、看病、等 异常处理方法。 java异常处理机制是我们java语言使用异常处理机制为程序提供了错误处理的能力,程序出现的错误,程序可以安全的退出,以保证程序正常的运行等

  7. Java Bean 作用域及它的几种类型介绍

    这篇文章主要介绍了Java Bean作用域及它的几种类型介绍,Spring框架作为一个管理Bean的IoC容器,那么Bean自然是Spring中的重要资源了,那Bean的作用域又是什么,接下来我们一起进入文章详细学习吧

  8. 面试突击之跨域问题的解决方案详解

    跨域问题本质是浏览器的一种保护机制,它的初衷是为了保证用户的安全,防止恶意网站窃取数据。那怎么解决这个问题呢?接下来我们一起来看

  9. Mybatis-Plus接口BaseMapper与Services使用详解

    这篇文章主要为大家介绍了Mybatis-Plus接口BaseMapper与Services使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  10. mybatis-plus雪花算法增强idworker的实现

    今天聊聊在mybatis-plus中引入分布式ID生成框架idworker,进一步增强实现生成分布式唯一ID,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

返回
顶部