Java程序有的时候在主线程中会创建多个线程去执行任务,然后在主线程执行完毕之前,把所有线程的任务进行汇总,以前可以用线程的join方法,但是这个方法不够灵活,我们可以使用CountDownLatch类,实现更优雅,而且使用线程池的话,可没有办法调用线程的join方法的呀!

一.简单使用CountDownLatch

  直接使用线程:

package com.example.demo.study;

import java.util.concurrent.CountDownLatch;

public class Study0215 {
//这里相当于新建一个初始值为2的计数器
private static volatile CountDownLatch countDownLatch = new CountDownLatch(2); public static void main(String[] args) throws InterruptedException { new Thread(()->{
try {
Thread.sleep(1000);
System.out.println("线程一执行完毕");
} catch (Exception e) { }finally {
//每调用这个方法计数器减一
countDownLatch.countDown();
} }).start(); new Thread(()->{
try {
Thread.sleep(1000);
System.out.println("线程二执行完毕");
} catch (Exception e) { }finally {
countDownLatch.countDown();
} }).start(); System.out.println("两个线程已经全部启动");
//只要调用了这个方法之后,主线程会阻塞,直到计数器countDownLatch变成0就会返回
countDownLatch.await();
System.out.println("执行完毕"); } }

  实际中尽量少直接操作线程,而是使用线程池:

package com.example.demo.study;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; public class Study0215 {
// 这里相当于新建一个初始值为2的计数器
private static volatile CountDownLatch countDownLatch = new CountDownLatch(2); public static void main(String[] args) throws InterruptedException {
//创建线程池
ExecutorService pool = Executors.newFixedThreadPool(2);
//将任务一丢进线程池
pool.submit(() -> {
try {
Thread.sleep(1000);
System.out.println("线程一执行完毕");
} catch (Exception e) { } finally {
// 每调用这个方法计数器减一
countDownLatch.countDown();
}
});
//任务二丢进线程池
pool.submit(() -> {
try {
Thread.sleep(1000);
System.out.println("线程二执行完毕");
} catch (Exception e) { } finally {
countDownLatch.countDown();
}
}); System.out.println("两个线程已经全部启动");
// 只要调用了这个方法之后,主线程会阻塞,直到计数器countDownLatch变成0就会返回
countDownLatch.await();
System.out.println("执行完毕"); } }

二.await方法

  看下面的图,可以知道这个CountDownLatch类内部有个工具类Sync实现了AQS,然后CountDownLatch中的方法都是调用工具类Sync去操作的,emmm....跟前面说过的ReentrantLock类结构是一样的;

  我们看看CountDownLatch构造器传递的数其实就是设置AQS中state的值:

//实际上调用把值传递给了Sync,也就是设置了AQS中的state
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
Sync(int count) {
setState(count);
}

 

  我们再看看await方法:

//当前线程调用了await方法之后,当前线程就会给阻塞,直到以下两种情况:
//1.其他线程调用了countDown方法将计数器减到0之后,该线程就返回了;
//2.其他线程调用了当前的线程的中断方法,当前线程抛出异常InterruptedException
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
} public final void acquireSharedInterruptibly(int arg) throws InterruptedException {
//当前线程被中断就抛出异常
if (Thread.interrupted())
throw new InterruptedException();
//查看计数器中的值是不是0,不过不是0,就进入AQS等待队列等待;
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
} protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}

三.countDown方法

public void countDown() {
sync.releaseShared(1);
} public final boolean releaseShared(int arg) {
//tryReleaseShared方法返回false,说明当前计数器的值减一成功
//返回true,说明计数器的值此时为0,那就要唤醒因为调用了CountDownLatch而阻塞的线程
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
} protected boolean tryReleaseShared(int releases) {
//一个无限循环
for (;;) {
//获取state的值
int c = getState();
//如果state为0,返回false
if (c == 0)
return false;
//否则就把state减一然后用CAS更新到state
int nextc = c-1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}

四.getState方法

  这个方法获取计数器的值,其实就是获取AQS中的state的值;

int getCount() {
return getState();
}
protected final int getState() {
return state;
}

  其实CountDownLatch比较容易,功能和Thread的join方法一样,只不过更灵活,基于AQS实现,在初始化的时候设置state的值,当线程调用CountDownLatch的await方法的时候,当前线程就会被丢到AQS的阻塞队列挂起;然后当其他线程调用了countDown方法,其实就是将state减一,当state等于0的时候,就会唤醒所有因为调用await方法而阻塞的线程;

线程同步器CountDownLatch的更多相关文章

  1. Semaphore控制同时访问的线程个数countdownlatch等待多个线程执行完本身线程再执行

    Semaphore控制同时访问的线程个数countdownlatch等待多个线程执行完本身线程再执行 Semaphore控制同时访问的线程个数countdownlatch等待多个线程执行完本身线程再执 ...

  2. 线程:CountDownLatch同步工具

    一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待. 类似计数器,当计数器的值为0时,继续往下执行. package ch03; import java.util.R ...

  3. Java多线程-两种常用的线程计数器CountDownLatch和循环屏障CyclicBarrier

    Java多线程编程-(1)-线程安全和锁Synchronized概念 Java多线程编程-(2)-可重入锁以及Synchronized的其他基本特性 Java多线程编程-(3)-从一个错误的双重校验锁 ...

  4. 线程等待——CountDownLatch使用

    告警性能优化过程中,遇到如下问题:1. 在数据库计算几十万个实体的KPI值的方差:2. 计算结果进行表格化处理. 这里KPI包含多个Counter的数据库函数运算(比如Decode,AVG等函数),方 ...

  5. 10.线程通信CountDownLatch

    CountDownLatch 1.一个同步的辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个.多个线程去一直等待,用给定的计数.初始化“CountDownLatch”. 由于调用 count ...

  6. 线程协作-CountDownLatch

    CountDownLatch允许一个或多个线程等待其他线程完成操作.

  7. Java线程:CountDownLatch 与Thread 的 join()

    需求: 主程序中需要等待所有子线程完成后 再继续任务 两种实现方式: 一种使用join() 方法:当在当前线程中调用某个线程 thread 的 join() 方法时,当前线程就会阻塞,直到thread ...

  8. 11.线程通信CountDownLatch

    package demo2; import java.util.concurrent.CountDownLatch; /** * Created by liudan on 2017/7/27. */ ...

  9. 线程同步-CountDownLatch

    应用场景: 有一个任务想要往下执行,但必须要等到其他的任务执行完毕后才可以继续往下执行. 假如我们这个想要继续往下执行的任务调用一个CountDownLatch对象的await()方法,其他的任务执行 ...

随机推荐

  1. springboot web - 启动(1) 创建SpringApplication

    一. 测试代码 @SpringBootApplication public class SbmvcApplication { public static void main(String[] args ...

  2. Spring Boot源码(二):SPI去除web.xml

    SPI广泛用于dubbo,spring boot,spring cloud alibaba等 关于SPI,可见SPI-Service Provider Interface 继续上篇文章 上面三句代码的 ...

  3. BZOJ 4238: 电压 DFS树

    分类讨论一下奇环和偶环的情况. code: #include <bits/stdc++.h> #define N 200006 #define setIO(s) freopen(s&quo ...

  4. Java第三节课总结

    动手动脑1: package ketangceshia;import java.util.Random;public class fuben {    public static void main( ...

  5. 2019kali中文乱码

    1.安装KALI2019.4版本后会出现乱码问题 2.更新国内源,使用vim编辑器修改:vim /etc/apt/sources.list添加 #清华大学 [更新源]    deb https://m ...

  6. 关于整合ssh中的细节03

    关于spring中提供的一些工具类和监听介绍 一.spring提供了一个HibernateTemplate类 ①HibernateTemplate类: 用于操作PO对象,类似Hibernate Ses ...

  7. eclipse中创建了web项目,src下创建子目录是平级的情况

    1.在以下可设置不同的视图 windows->show view菜单 ->点Other......    然后在搜索框里输入你想要的视图 2.在Project Explorer下创建的包看 ...

  8. RHEL/CentOS 7中Nginx的systemd service

    源码安装的nginx ,没有systemd service 管理 nginx 下面教程,告诉你如何设置nginx 的systemd service nginx systemd的服务文件是/usr/li ...

  9. ReviewBoard使用:添加SVN

    1.登录ReviewBoard,选择“Admin” 2.选择 “Repositores”,点击按钮“Add” 3.填写内容,然后点击按钮“SAVE”保存 Name:仓库名称(自己随意写) Hostin ...

  10. [Note]prufer

    [Note]Prufer编码 实现 不断删除度数为\(1\)的最小序号的点,并输出与其相连的节点的序号,直至树中只有两个节点. 性质 任何一棵\(n\)节点的树都可以唯一的用长度为\(n-2\)的pr ...