CountDownLatch的2个用途:

1. 所有线程都到达相同的起跑线后,再一起开始跑(并非同时开始,而是队列中一个唤醒另一个)【此情况需到达起跑线后再调用await()等待其他线程】

2. 所有线程都到达终点(执行完)后,再一起庆祝 (并非同时开始,而是队列中一个唤醒另一个)【此情况需到达起终点后再调用await()等待其他线程】

package com.study.concurrent_utils;

import java.util.concurrent.CountDownLatch;

public class Test_CountDownLatch {

	/*
* 没隔1s开启一个线程,共开启6个线程
* 若希望6个线程 同时 执行某一操作
* 可以用CountDownLatch实现
*/
public static void test01() throws InterruptedException {
CountDownLatch ctl = new CountDownLatch(6); for (int i = 0; i < 6; i++) {
new Thread() {
@Override
public void run() {
ctl.countDown();
try {
ctl.await();
// 6个线程都启动执行到此处时,打印如下
System.out.println("here I am...");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
Thread.sleep(1000L);
}
} /*
* 开启6个线程,6个线程都执行完后,才执行某个操作
* 可以用CountDownLatch来实现
*/
public static void test02() throws InterruptedException {
JamesCountDownLatch ctl = new JamesCountDownLatch(6); for (int i = 0; i < 6; i++) {
new Thread() {
@Override
public void run() {
System.out.println("after print...");
ctl.countDown();
}
}.start();
Thread.sleep(1000L);
} ctl.await();
// 6条线程都执行完后同时打印这句话
System.out.println("main thread do something ..."); } public static void main(String args[]) throws InterruptedException {
test02();
}
}

CountDownLatch实际应用

在zookeeper的master选举机制中,守护线程监听zk节点删除事件,一旦触发,CountDownLatch减1变成0,即触发线程继续递归调用选举逻辑

import java.util.concurrent.CountDownLatch;
import org.I0Itec.zkclient.IZkDataListener;
import org.I0Itec.zkclient.ZkClient;
import org.I0Itec.zkclient.exception.ZkNodeExistsException; public class MasterElectionDemo { static class Server { private String cluster, name, address; private final String path, value; private String master; public Server(String cluster, String name, String address) {
super();
this.cluster = cluster;
this.name = name;
this.address = address;
path = "/" + this.cluster + "Master";
value = "name:" + name + " address:" + address; ZkClient client = new ZkClient("localhost:2181");
client.setZkSerializer(new MyZkSerializer()); new Thread(new Runnable() { @Override
public void run() {
electionMaster(client);
} }).start();
} public void electionMaster(ZkClient client) {
try {
client.createEphemeral(path, value);
master = client.readData(path);
System.out.println(value + "创建节点成功,成为Master");
} catch (ZkNodeExistsException e) {
master = client.readData(path);
System.out.println("Master为:" + master);
} // 为阻塞自己等待而用
CountDownLatch cdl = new CountDownLatch(1); // 注册watcher
IZkDataListener listener = new IZkDataListener() { @Override
public void handleDataDeleted(String dataPath) throws Exception {
System.out.println("-----监听到节点被删除");
cdl.countDown();
} @Override
public void handleDataChange(String dataPath, Object data) throws Exception { }
}; client.subscribeDataChanges(path, listener); // 让自己阻塞
if (client.exists(path)) {
try {
cdl.await();
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
// 醒来后,取消watcher
client.unsubscribeDataChanges(path, listener);
// 递归调自己(下一次选举)
electionMaster(client); } } public static void main(String[] args) {
// 测试时,依次开启多个Server实例java进程,然后停止获取的master的节点,看谁抢到Master
// Server s = new Server("cluster1", "server1", "192.168.1.11:8991");
// Server s = new Server("cluster1", "server2", "192.168.1.11:8992");
Server s = new Server("cluster1", "server3", "192.168.1.11:8993");
// Server s = new Server("cluster1", "server4", "192.168.1.11:8994");
} }

手写CountDownLatch(基于AQS)

countDown()方法:释放共享锁,首先会尝试释放共享锁(其实际是做CAS操作将state减1,如果state减到了0,返回true),如果返回true,说明读锁已释放完,则将等待队列头部线程唤醒。

await()方法:获取共享锁,首先会尝试获取共享锁(其实际操作,获取并判断state值:return getState()==0 ? 1: -1;),若state不是0,即所有线程还没到齐,集体活动还不能开始,此时将其加入等待队列,并且开始自旋,不断判断自己是不是队列头部,即下一个开始跑的是不是自己,是的话就再次尝试获取共享锁,若失败就将自己挂起,若成功即从等待队列移除,并唤醒下一个要获取共享锁的线程。

package com.study.concurrent_utils;

import java.util.concurrent.locks.AbstractQueuedSynchronizer;

public class JamesCountDownLatch {

	private Sync sync;

	public JamesCountDownLatch(int count) {
sync = new Sync(count);
} public void countDown() {
sync.releaseShared(1);
} public void await() {
sync.acquireShared(1);
} class Sync extends AbstractQueuedSynchronizer {
public Sync(int count) {
setState(count);
} @Override
protected int tryAcquireShared(int arg) {
// 只有当state变为0时,加锁成功
return getState() == 0 ? 1 : -1;
} /*
* countdown的方法
*/
@Override
protected boolean tryReleaseShared(int arg) {
for (;;) {
int c = getState();
if (c == 0)
return false;
int nextc = c - arg;
// 用CAS操作,讲count减一
if (compareAndSetState(c, nextc)) {
// 当state=0时,释放锁成功,返回true
return nextc == 0;
}
}
}
}
}

手写Semaphore(基于AQS)

package com.study.concurrent_utils;

import java.util.concurrent.locks.AbstractQueuedSynchronizer;

public class JamesSemaphore {

	private Sync sync;

	public JamesSemaphore(int state) {
sync = new Sync(state);
} public void acquire() {
sync.acquireShared(1);
} public void release() {
sync.releaseShared(1);
} class Sync extends AbstractQueuedSynchronizer {
int state; public Sync(int state) {
this.state = state;
} @Override
protected int tryAcquireShared(int arg) {
for (;;) {
int available = getState();
int remaining = available - arg;
if (remaining < 0 || compareAndSetState(available, remaining))
return remaining;
}
} @Override
protected boolean tryReleaseShared(int arg) {
for (;;) {
int current = getState();
int next = current + arg;
if (next < current) // overflow
throw new Error("Maximum permit count exceeded");
if (compareAndSetState(current, next))
return true;
}
}
}
}

package com.study.concurrent_utils;

import java.util.concurrent.CyclicBarrier;

public class TestCyclicBarrier {
public static void main(String[] args) throws InterruptedException {
CyclicBarrier cyclicBarrier = new CyclicBarrier(3, new Runnable() {
@Override
public void run() {
System.out.println(">>>>3个已满,走起<<<");
}
}); for (int i = 0; i < 30; i++) {
new Thread(new Runnable() {
@Override
public void run() {
try {
cyclicBarrier.await();
System.out.println(Thread.currentThread() + ":start...");
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
Thread.sleep(1000L);
}
}
}
>>>>3个已满,走起<<<
Thread[Thread-2,5,main]:start...
Thread[Thread-0,5,main]:start...
Thread[Thread-1,5,main]:start...
>>>>3个已满,走起<<<
Thread[Thread-5,5,main]:start...
Thread[Thread-3,5,main]:start...
Thread[Thread-4,5,main]:start...
>>>>3个已满,走起<<<
Thread[Thread-8,5,main]:start...
Thread[Thread-6,5,main]:start...
Thread[Thread-7,5,main]:start...

手写CyclicBarrier(基于ReentrantLock)

ReentrantLock的Condition就是一个等待队列,ReentrantLock是一个可重入锁

package com.study.concurrent_utils;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock; public class JamesCyclicBarrier { private final ReentrantLock lock = new ReentrantLock();
private final Condition condition = lock.newCondition(); // 记录当前这个批次有多少个
private int count = 0; // 记录批次的大小
private final int parties; // 分代
private Object generation = new Object(); public JamesCyclicBarrier(int parties) {
if (parties <= 0)
throw new IllegalArgumentException();
this.parties = parties;
} // 进入下一个分代
public void nextGeneration() {
condition.signalAll();
count = 0;
generation = new Object();
} public void await() {
// 实现排队,需要将线程放到等待队列
// 还需要将线程挂起
//
final ReentrantLock lock = this.lock;
lock.lock();
try {
// 记录当前的generation,相当于记录当前批次的id
final Object g = generation; int index = ++count;
// 批次已经达到parties,
if (index == parties) {
// 进入下一个批次
nextGeneration();
return;
} // 若未达到批次,就进入等待
for (;;) {
try {
condition.await();
} catch (InterruptedException e) { }
if (g != generation) {
return;
}
} } finally {
lock.unlock();
}
}
}

Future/Runnable

package com.study.futuretask;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.locks.LockSupport; public class Demo3_CallableTest {
public static void main(String args[]) throws InterruptedException, ExecutionException {
CallTask cTask = new CallTask();
JamesFutureTask<String> fTask = new JamesFutureTask<String>(cTask); // 执行第一次
Thread th = new Thread(fTask);
th.start(); System.out.println("begain to get...");
String result = fTask.get();
System.out.println(result); // 执行第二次,失败
Thread th1 = new Thread(fTask);
th1.start();
}
} class CallTask implements Callable<String> { @Override
public String call() throws Exception {
LockSupport.parkNanos(1000 * 1000 * 1000 * 5L);
System.out.println("done...");
return "James";
}
}

手写FutureTask

package com.study.futuretask;

import java.util.concurrent.Callable;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.LockSupport; public class JamesFutureTask<T> implements Runnable { // future只能执行一次
private volatile int state = NEW;
private static final int NEW = 0;
private static final int RUNNING = 1;
private static final int FINISED = 2; public JamesFutureTask(Callable<T> task) {
this.callable = task;
} // 程序执行的结果
private T result; // 要自行的task
Callable<T> callable; // 获取结果的线层等待队列
LinkedBlockingQueue<Thread> waiters = new LinkedBlockingQueue<>(100); // 执行当前FutureTask的线程,用CAS进行争抢
AtomicReference<Thread> runner = new AtomicReference<>(); @Override
public void run() {
// 判断当前对象的状态,如果是New就执行,如果
if (state != NEW || !runner.compareAndSet(null, Thread.currentThread()))
return;
state = RUNNING; try {
result = callable.call();
} catch (Exception e) {
e.printStackTrace();
} finally {
state = FINISED;
} // 方法执行完,唤醒所有线程
while (true) {
Thread waiter = waiters.poll();
if (waiter == null)
break;
LockSupport.unpark(waiter);
} } public T get() {
if (state != FINISED) {
waiters.offer(Thread.currentThread());
} while (state != FINISED) {
LockSupport.park();
} return result;
}
}

1.3.4 并发工具类CountDownLatch/Semaphore/CyclicBarrier/FutureTask的更多相关文章

  1. Java中的并发工具类(CountDownLatch、CyclicBarrier、Semaphore、Exchanger)

    在JDK的并发包里提供了很多有意思的并发工具类.CountDownLatch.CyclicBarrier和Semaphore 工具类提供了一种并发流程控制的手段,Exchanger 工具类则提供了在线 ...

  2. 25.大白话说java并发工具类-CountDownLatch,CyclicBarrier,Semaphore,Exchanger

    1. 倒计时器CountDownLatch 在多线程协作完成业务功能时,有时候需要等待其他多个线程完成任务之后,主线程才能继续往下执行业务功能,在这种的业务场景下,通常可以使用Thread类的join ...

  3. java多线程10:并发工具类CountDownLatch、CyclicBarrier和Semaphore

    在JDK的并发包(java.util.concurrent下)中给开发者提供了几个非常有用的并发工具类,让用户不需要再去关心如何在并发场景下写出同时兼顾线程安全性与高效率的代码. 本文分别介绍Coun ...

  4. 多线程学习笔记六之并发工具类CountDownLatch和CyclicBarrier

    目录 简介 CountDownLatch 示例 实现分析 CountDownLatch与Thread.join() CyclicBarrier 实现分析 CountDownLatch和CyclicBa ...

  5. JAVA并发工具类---------------(CountDownLatch和CyclicBarrier)

    CountDownLatch是什么 CountDownLatch,英文翻译为倒计时锁存器,是一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待. 闭锁可以延迟线程的进 ...

  6. Java 并发工具类 CountDownLatch、CyclicBarrier、Semaphore、Exchanger

    本文部分摘自<Java 并发编程的艺术> CountDownLatch CountDownLatch 允许一个或多个线程等待其他线程完成操作.假设现有一个需求:我们需要解析一个 Excel ...

  7. Java中的4个并发工具类 CountDownLatch CyclicBarrier Semaphore Exchanger

    在 java.util.concurrent 包中提供了 4 个有用的并发工具类 CountDownLatch 允许一个或多个线程等待其他线程完成操作,课题点 Thread 类的 join() 方法 ...

  8. Java并发工具类 - CountDownLatch

    Java并发工具类 - CountDownLatch 1.简介 CountDownLatch是Java1.5之后引入的Java并发工具类,放在java.util.concurrent包下面 http: ...

  9. Java并发(十五):并发工具类——信号量Semaphore

    先做总结: 1.Semaphore是什么? Semaphore(信号量)是用来控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源. 把它比作是控制流量的红绿灯,比如XX马路要 ...

随机推荐

  1. Java进阶知识19 Struts2和Spring整合在一起

    1.概述 1.Spring负责对象创建  2.Struts2负责用Action处理请求  3.整合的关键点:让Struts2框架Action对象的创建交给Spring完成. 2.整合实例 需要用到的 ...

  2. centos7下用kubeadm安装k8s集群并使用ipvs做高可用方案

    1.准备 1.1系统配置 在安装之前,需要先做如下准备.三台CentOS主机如下: 配置yum源(使用腾讯云的) 替换之前先备份旧配置 mv /etc/yum.repos.d/CentOS-Base. ...

  3. Jenkins 插件加速下载

    本文主旨 看到好多加速Jenkins安装插件速度的文章,大多数教程中都是在插件配置里使用 https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/up ...

  4. JS的十大排序算法

     名词解释: n: 数据规模k:“桶”的个数In-place: 占用常数内存,不占用额外内存Out-place: 占用额外内存稳定性:排序后2个相等键值的顺序和排序之前它们的顺序相同 冒泡排序(Bub ...

  5. Linux上命令行检出、提交和更新操作

    1.创建工作区目录 列:我创建两个工作目录,用来模拟两个开发人员,命令如下:(工作路径可以按照自己需要随意改变) mkdir -p /root/workspace/harry mkdir -p /ro ...

  6. Flutter中用ListView嵌套GridView报错异常

    flutter中的ListView组件和GridView组件都是常用的布局组件,有时候ListView中需要嵌套GridView来使用,例如下图: 这种情况就需要在ListView里面再嵌套一个Gri ...

  7. (main)贝叶斯统计 | 贝叶斯定理 | 贝叶斯推断 | 贝叶斯线性回归 | Bayes' Theorem

    2019年08月31日更新 看了一篇发在NM上的文章才又明白了贝叶斯方法的重要性和普适性,结合目前最火的DL,会有意想不到的结果. 目前一些最直觉性的理解: 概率的核心就是可能性空间一定,三体世界不会 ...

  8. Visual Studio Team Systems

    https://www.cnblogs.com/33568639/archive/2008/12/29/1364222.html https://baike.sogou.com/v7818386.ht ...

  9. linux里面源码安装imagemagick库

    在搞树莓派的时候想搞一下树莓派中摄像头获取图像之后传给安卓,安卓进行展示. 恰好用到了imagemagick这个库,我就像正常一样进行安装,sudo apt-get install Imagick 但 ...

  10. kotlin之函数的范围和泛型函数

    kotlin 中函数可以定义为局部函数,成员函数以及扩展函数 局部函数:就是嵌套在函数内的函数 成员函数就是定义在类或者对象之内的函数 泛型函数就是函数可以带有泛型参数,可通过尖括号来指定