Java CountDownLatch工作原理详解是怎样的?

2026-05-26 00:511阅读0评论SEO资源
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计1651个文字,预计阅读时间需要7分钟。

Java CountDownLatch工作原理详解是怎样的?

目录 + CountDownLatch原理解析

1.demo展示

2.原理解析

Java CountDownLatch学习总结 + 来源包 + 业务书写示例 + 一般代码示例CountDownLatch原理解析

1.demo展示

2.代码逻辑展示:主线程中创建了2个子线程分别执行不同的任务

目录
  • CountDownLatch原理解析
    • 1. demo展示
    • 2. 原理解析
  • Java CountDownLatch学习总结
    • 来源包
    • 业务书写示例
    • 一般代码示例

Java CountDownLatch工作原理详解是怎样的?

CountDownLatch原理解析

1. demo展示

代码逻辑展示了主线程中创建2个子线程分别去执行任务,主线程等2个子线程执行完毕后,再接着执行下面的代码;

常用场景:

分别计算,汇总结果。如,多个线程分别解析excel中的sheet,等待全部解析完毕后汇总结果;

import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; public class CountDownLatchDemo { //定义一个倒计时闩锁 static CountDownLatch c = new CountDownLatch(2); public static void main(String[] args) throws InterruptedException { new Thread(() -> { try { TimeUnit.MICROSECONDS.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("我是线程1"); //释放一个 c.countDown(); }).start(); new Thread(() -> { try { TimeUnit.MICROSECONDS.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("我是线程2"); //释放一个 c.countDown(); }).start(); System.out.println("我是主线程,我要等那两个线程执行完毕..."); //等待倒计时为0 c.await(); System.out.println("我是主线程,那两个线程都执行完了"); } }

输出:

我是主线程,我要等那两个线程执行完毕...

我是线程2

我是线程1

我是主线程,那两个线程都执行完了

2. 原理解析

1.先看构造函数new CountDownLatch(2)做了什么?

这是初始化了AQS子类,并将AQS的状态state设置为传入的2;

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

2.看c.countDown()做了什么?

它释放了一个共享锁状态,也就是state减1;

public void countDown() { sync.releaseShared(1); }

3.再看c.await()做了什么?

await方法是CounDownLatch中定义的,它调用了其内部类Sync(也是AQS的子类)的获取共享锁的方法acquireSharedInterruptibly;

acquireSharedInterruptibly方法中调用了CountDownLatch内部类Sync中实现的获取共享锁的方法tryAcquireShared,返回值不小0就算获取到了锁,await方法就能返回了,如果返回值小于0将会进入阻塞等待;

CountDownLatch内部类Sync中tryAcquireShared的实现很简单,只要state=0就返回1,否则返回-1;上面说了返回一个不小于0的数字,c.await()就相当于获取到了锁,就可以返回了,主线程就可以继续执行了。

通过上面分析,每次c.countDown(),就会将state减1,state=0的时候主线程恢复执行;

Java CountDownLatch学习总结

来源包

同为 java.util.concurrent 下的,即也是并发多线程相关下的类,直译 “倒计时锁存器”,一般用于多线程场景,单一的线程也可以,用于等待多个任务完成后再执行其他操作;

提供方法

await()

  • 导致当前线程等待,直到锁存器倒数到零,除非该线程是{@linkplain Thread35;interrupt interrupted}即被打断状态。
  • 如果当前计数为零,则此方法立即返回。
  • 如果当前计数大于零,则当前线程将出于线程调度目的被禁用,并处于休眠状态,直到发生以下两种情况之一:
  • 由于调用{@link#countDown}方法,计数达到零;或者其他线程{@linkplain thread#中断}当前线程。

如果当前线程:

  • 在进入此方法时设置了其中断状态;或者
  • 在等待时{@linkplain Thread#interrupt interrupted},
  • 则抛出{@link InterruptedException},并清除当前线程的中断状态。

简单说就是当使用了这个方法后当前这一个线程将进入等待状态,直到计数器被减到0或者当前线程被中断,计数器被减到0后,所有等待的线程将被唤醒继续向下执行

await(long timeout, TimeUnit unit)

同上,但是指定了等待的超时时间,即线程除了上方两种被唤醒的情况下,等待到超时时间后也会被唤醒

  • countDown():当前计数器减一,如果如果减到 0 则唤醒所有等待在这个 CountDownLatch 上的线程。
  • getCount():获取当前计数的数值

业务书写示例

即将需要一会儿处理的业务 list 设置为计数器的大小,

然后对里面的业务数据执行异步操作,处理业务过程中不论是否有异常都需要对计数器减一,最终使用 await 等待所有任务执行完成,执行完成后,将进入后续处理

            final CountDownLatch latch = new CountDownLatch(lists.size());                          for (List<JSONObject> item: lists) {                 executor.submit(new Runnable() {                     @Override                     public void run() {                         // ....... 业务处理                         } catch (Exception e) {                             // 异常处理                         } finally {                             latch.countDown();                         }                     }                 });             }             try {                 latch.await();             } catch (InterruptedException e) {                 log.error("线程被中断", e);             }        // lists 处理完成后的其他业务操作

一般代码示例

public static void main(String[] args) throws InterruptedException {         final CountDownLatch downLatch = new CountDownLatch(3);         Await wait111 = new Await("wait111", downLatch);         Await wait222 = new Await("wait222", downLatch);         CountDownStart countDownStart = new CountDownStart(downLatch);         wait111.start();         wait222.start();         Thread.sleep(1000);         countDownStart.run();     } class Await extends Thread{     private CountDownLatch countDownLatch;     private String name;     public Await(String name, CountDownLatch countDownLatch){         this.name = name;         this.countDownLatch = countDownLatch;     }     @Override     public void run() {         System.out.println(name + " start.....");         System.out.println(name + " run.....");         try {             countDownLatch.await();         } catch (InterruptedException e) {             e.printStackTrace();         }         System.out.println(name + " continue.....run");     } } class CountDownStart extends Thread{     private CountDownLatch countDownLatch;     public CountDownStart(CountDownLatch countDownLatch){         this.countDownLatch = countDownLatch;     }     @Override     public void run() {         countDownLatch.countDown();         countDownLatch.countDown();         countDownLatch.countDown();         System.out.println("start countDown");     } }

运行结果:

wait222 start.....
wait222 run.....
wait111 start.....
wait111 run.....
start countDown
wait111 continue.....run
wait222 continue.....run

但是当我把线程等待去除后:

    public static void main(String[] args) throws InterruptedException {         final CountDownLatch downLatch = new CountDownLatch(3);         Await wait111 = new Await("wait111", downLatch);         Await wait222 = new Await("wait222", downLatch);         CountDownStart countDownStart = new CountDownStart(downLatch);         wait111.start();         wait222.start(); //        Thread.sleep(1000);         countDownStart.run();     }

结果:

start countDown
wait111 start.....
wait111 run.....
wait111 continue.....run
wait222 start.....
wait222 run.....
wait222 continue.....run

另外两个线程线程并没有开始就执行,可能被抢占了,也可能调度优先度不同,实际使用时还是需要多多实验

以上为个人经验,希望能给大家一个参考,也希望大家多多支持自由互联。

本文共计1651个文字,预计阅读时间需要7分钟。

Java CountDownLatch工作原理详解是怎样的?

目录 + CountDownLatch原理解析

1.demo展示

2.原理解析

Java CountDownLatch学习总结 + 来源包 + 业务书写示例 + 一般代码示例CountDownLatch原理解析

1.demo展示

2.代码逻辑展示:主线程中创建了2个子线程分别执行不同的任务

目录
  • CountDownLatch原理解析
    • 1. demo展示
    • 2. 原理解析
  • Java CountDownLatch学习总结
    • 来源包
    • 业务书写示例
    • 一般代码示例

Java CountDownLatch工作原理详解是怎样的?

CountDownLatch原理解析

1. demo展示

代码逻辑展示了主线程中创建2个子线程分别去执行任务,主线程等2个子线程执行完毕后,再接着执行下面的代码;

常用场景:

分别计算,汇总结果。如,多个线程分别解析excel中的sheet,等待全部解析完毕后汇总结果;

import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; public class CountDownLatchDemo { //定义一个倒计时闩锁 static CountDownLatch c = new CountDownLatch(2); public static void main(String[] args) throws InterruptedException { new Thread(() -> { try { TimeUnit.MICROSECONDS.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("我是线程1"); //释放一个 c.countDown(); }).start(); new Thread(() -> { try { TimeUnit.MICROSECONDS.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("我是线程2"); //释放一个 c.countDown(); }).start(); System.out.println("我是主线程,我要等那两个线程执行完毕..."); //等待倒计时为0 c.await(); System.out.println("我是主线程,那两个线程都执行完了"); } }

输出:

我是主线程,我要等那两个线程执行完毕...

我是线程2

我是线程1

我是主线程,那两个线程都执行完了

2. 原理解析

1.先看构造函数new CountDownLatch(2)做了什么?

这是初始化了AQS子类,并将AQS的状态state设置为传入的2;

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

2.看c.countDown()做了什么?

它释放了一个共享锁状态,也就是state减1;

public void countDown() { sync.releaseShared(1); }

3.再看c.await()做了什么?

await方法是CounDownLatch中定义的,它调用了其内部类Sync(也是AQS的子类)的获取共享锁的方法acquireSharedInterruptibly;

acquireSharedInterruptibly方法中调用了CountDownLatch内部类Sync中实现的获取共享锁的方法tryAcquireShared,返回值不小0就算获取到了锁,await方法就能返回了,如果返回值小于0将会进入阻塞等待;

CountDownLatch内部类Sync中tryAcquireShared的实现很简单,只要state=0就返回1,否则返回-1;上面说了返回一个不小于0的数字,c.await()就相当于获取到了锁,就可以返回了,主线程就可以继续执行了。

通过上面分析,每次c.countDown(),就会将state减1,state=0的时候主线程恢复执行;

Java CountDownLatch学习总结

来源包

同为 java.util.concurrent 下的,即也是并发多线程相关下的类,直译 “倒计时锁存器”,一般用于多线程场景,单一的线程也可以,用于等待多个任务完成后再执行其他操作;

提供方法

await()

  • 导致当前线程等待,直到锁存器倒数到零,除非该线程是{@linkplain Thread35;interrupt interrupted}即被打断状态。
  • 如果当前计数为零,则此方法立即返回。
  • 如果当前计数大于零,则当前线程将出于线程调度目的被禁用,并处于休眠状态,直到发生以下两种情况之一:
  • 由于调用{@link#countDown}方法,计数达到零;或者其他线程{@linkplain thread#中断}当前线程。

如果当前线程:

  • 在进入此方法时设置了其中断状态;或者
  • 在等待时{@linkplain Thread#interrupt interrupted},
  • 则抛出{@link InterruptedException},并清除当前线程的中断状态。

简单说就是当使用了这个方法后当前这一个线程将进入等待状态,直到计数器被减到0或者当前线程被中断,计数器被减到0后,所有等待的线程将被唤醒继续向下执行

await(long timeout, TimeUnit unit)

同上,但是指定了等待的超时时间,即线程除了上方两种被唤醒的情况下,等待到超时时间后也会被唤醒

  • countDown():当前计数器减一,如果如果减到 0 则唤醒所有等待在这个 CountDownLatch 上的线程。
  • getCount():获取当前计数的数值

业务书写示例

即将需要一会儿处理的业务 list 设置为计数器的大小,

然后对里面的业务数据执行异步操作,处理业务过程中不论是否有异常都需要对计数器减一,最终使用 await 等待所有任务执行完成,执行完成后,将进入后续处理

            final CountDownLatch latch = new CountDownLatch(lists.size());                          for (List<JSONObject> item: lists) {                 executor.submit(new Runnable() {                     @Override                     public void run() {                         // ....... 业务处理                         } catch (Exception e) {                             // 异常处理                         } finally {                             latch.countDown();                         }                     }                 });             }             try {                 latch.await();             } catch (InterruptedException e) {                 log.error("线程被中断", e);             }        // lists 处理完成后的其他业务操作

一般代码示例

public static void main(String[] args) throws InterruptedException {         final CountDownLatch downLatch = new CountDownLatch(3);         Await wait111 = new Await("wait111", downLatch);         Await wait222 = new Await("wait222", downLatch);         CountDownStart countDownStart = new CountDownStart(downLatch);         wait111.start();         wait222.start();         Thread.sleep(1000);         countDownStart.run();     } class Await extends Thread{     private CountDownLatch countDownLatch;     private String name;     public Await(String name, CountDownLatch countDownLatch){         this.name = name;         this.countDownLatch = countDownLatch;     }     @Override     public void run() {         System.out.println(name + " start.....");         System.out.println(name + " run.....");         try {             countDownLatch.await();         } catch (InterruptedException e) {             e.printStackTrace();         }         System.out.println(name + " continue.....run");     } } class CountDownStart extends Thread{     private CountDownLatch countDownLatch;     public CountDownStart(CountDownLatch countDownLatch){         this.countDownLatch = countDownLatch;     }     @Override     public void run() {         countDownLatch.countDown();         countDownLatch.countDown();         countDownLatch.countDown();         System.out.println("start countDown");     } }

运行结果:

wait222 start.....
wait222 run.....
wait111 start.....
wait111 run.....
start countDown
wait111 continue.....run
wait222 continue.....run

但是当我把线程等待去除后:

    public static void main(String[] args) throws InterruptedException {         final CountDownLatch downLatch = new CountDownLatch(3);         Await wait111 = new Await("wait111", downLatch);         Await wait222 = new Await("wait222", downLatch);         CountDownStart countDownStart = new CountDownStart(downLatch);         wait111.start();         wait222.start(); //        Thread.sleep(1000);         countDownStart.run();     }

结果:

start countDown
wait111 start.....
wait111 run.....
wait111 continue.....run
wait222 start.....
wait222 run.....
wait222 continue.....run

另外两个线程线程并没有开始就执行,可能被抢占了,也可能调度优先度不同,实际使用时还是需要多多实验

以上为个人经验,希望能给大家一个参考,也希望大家多多支持自由互联。