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. 关于 DP 的一些内容

    0.关于         动态规划是编程解题的一种重要手段.1951 年美国数学家 R.Bellman 等人,根据一类多阶段问题的特点,把多阶段决策问题变换为一系列互相联系的单阶段问题,然后逐个加以解 ...

  2. Linux系统之网络相关的命令

    Linux系统之网络相关的命令 网络概述 网络:通过通信介质和通信设备 将分布不同地点的两台或多台计算机,经过相应的程序实现通信switch 交换机router 路由器网络的功能:数据通信:利用网络传 ...

  3. 为什么MYSQL分页时使用limit+ order by会出现数据重复问题

    问题描述: MYSQL采用limit进行翻页查询时,搭配order by ,在翻到第二页的时候可能会出现第一页的数据,  示例sql如下: select  a,b from c where d = ' ...

  4. AMC Problems and Solutions

    AMC Problems and Solutions:https://artofproblemsolving.com/wiki/index.php/AMC_Problems_and_Solutions ...

  5. Android中的内存管理机制以及正确的使用方式

    概述 从操作系统的角度来说,内存就是一块数据存储区域,属于可被操作系统调度的资源.现代多任务(进程)的操作系统中,内存管理尤为重要,操作系统需要为每一个进程合理的分配内存资源,所以可以从两方面来理解操 ...

  6. Android_侧滑菜单的实现

    1.创建侧滑菜单Fragment package com.example.didida_corder; import android.os.Bundle; import android.view.La ...

  7. window.location.herf传值问题

    各个值之间用&&&&&&连接 新版本的tomcat不支持其他字符,需要通过encodeURIComponent编码 变量名数字后不能直接加字母 such ...

  8. 在线教育厮杀惨烈,51Talk一家独大之后,却仍是持续亏损?

    编辑 | 于斌 出品 | 于见(mpyujian) 来自51Talk在线少儿英语专业的学生陈和涛已经成为最近<快乐营大本营>的一个亮点.该学生应邀以流利的英语在台上作自我介绍,并与51Ta ...

  9. 右键添加IDEA打开

    在安装IntelliJ IDEA时可能没有选择文件用idea打开的选项,现在有这个需求. 下面就演示一下,如何添加文件右键用idea打开! 1. 打开注册表 win+R键输入regedit 2. 找到 ...

  10. SSM+layui实现增删改查

    前端使用layui框架,后端使用Spring+SpringMVC+Mybatis的集合框架,数据库使用MySQL,完成对一张用户表的增删改查操作. 前后端分离开发,即前端的HTML页面通过ajax技术 ...