线程同步器CountDownLatch
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的更多相关文章
- Semaphore控制同时访问的线程个数countdownlatch等待多个线程执行完本身线程再执行
Semaphore控制同时访问的线程个数countdownlatch等待多个线程执行完本身线程再执行 Semaphore控制同时访问的线程个数countdownlatch等待多个线程执行完本身线程再执 ...
- 线程:CountDownLatch同步工具
一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待. 类似计数器,当计数器的值为0时,继续往下执行. package ch03; import java.util.R ...
- Java多线程-两种常用的线程计数器CountDownLatch和循环屏障CyclicBarrier
Java多线程编程-(1)-线程安全和锁Synchronized概念 Java多线程编程-(2)-可重入锁以及Synchronized的其他基本特性 Java多线程编程-(3)-从一个错误的双重校验锁 ...
- 线程等待——CountDownLatch使用
告警性能优化过程中,遇到如下问题:1. 在数据库计算几十万个实体的KPI值的方差:2. 计算结果进行表格化处理. 这里KPI包含多个Counter的数据库函数运算(比如Decode,AVG等函数),方 ...
- 10.线程通信CountDownLatch
CountDownLatch 1.一个同步的辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个.多个线程去一直等待,用给定的计数.初始化“CountDownLatch”. 由于调用 count ...
- 线程协作-CountDownLatch
CountDownLatch允许一个或多个线程等待其他线程完成操作.
- Java线程:CountDownLatch 与Thread 的 join()
需求: 主程序中需要等待所有子线程完成后 再继续任务 两种实现方式: 一种使用join() 方法:当在当前线程中调用某个线程 thread 的 join() 方法时,当前线程就会阻塞,直到thread ...
- 11.线程通信CountDownLatch
package demo2; import java.util.concurrent.CountDownLatch; /** * Created by liudan on 2017/7/27. */ ...
- 线程同步-CountDownLatch
应用场景: 有一个任务想要往下执行,但必须要等到其他的任务执行完毕后才可以继续往下执行. 假如我们这个想要继续往下执行的任务调用一个CountDownLatch对象的await()方法,其他的任务执行 ...
随机推荐
- Java教程-修炼
在2020这个特殊的时期,在家就是为国家做贡献!一切都会好起来的,加油中国!! Java 第一章 Java概述 1.1 Java历史 1.2 Java语言最主要的特点 1.3 Java环境搭建 1.3 ...
- TChart-数据编辑(TChartListBox,TeeCommander)
先上图 功能代码: unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Contr ...
- tensor的维度扩张的手段--Broadcasting
broadcasting是tensorflow中tensor维度扩张的最常用的手段,指对某一个维度上重复N多次,虽然它呈现数据已被扩张,但不会复制数据. 可以这样理解,对 [b,784]@[784,1 ...
- Java第二节课总结
Java的基本运行单位是类.类由数据成员和函数成员组成.变量的类型之间可以相互转换.String是一个类.static代表静态变量. 运行结果: false false ...
- ubuntu18.04图形模式切换到命令模式
命令模式 :Ctrl+Alt+F5 图形模式:Ctrl+Alt+F1
- 题解 AT5632 【Sum of Two Integers】
在幼儿园的时候,我们就学习过把一个数分成\(a\)与\(b\),我们只需要用计算机来模拟这个过程就可以了. 我们先从奇数开始看起,以\(5\)为例: 我们可以发现,\(5\)可以分成\(1\)和\(4 ...
- javascripts的this
前言 javascript 的 this 指向主要是依赖上下文对象决定,箭头函数例外. 默认绑定 在全局作用域下调用函数,函数的this默认指向window. 注意1:严格模式下,默认指向undefi ...
- pycharm+anaconda在Mac上的配置方法 2019.11.29
内心os: 听人说,写blog是加分项,那他就不是浪费时间的事儿了呗 毕竟自己菜还是留下来东西来自己欣赏吧 Mac小电脑上进行python数据开发环境的配置 首先下载Anaconda,一个超好用的数据 ...
- 如何查看oracle当前连接数,会话数
第一步,在cmd命令行,输入sqlplus 第二步,根据提示输入用户名与密码 1. 查看processes和sessions参数 SQL> show parameter processes NA ...
- git 命令 总结
1.添加所有文件 git add . 2.添加某个文件 git add filename 3.commit 注释 git commit -m'commit 注释' 4.修改commit 注释 git ...