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. 「51Nod 1601」完全图的最小生成树计数 「Trie」

    题意 给定\(n\)个带权点,第\(i\)个点的权值为\(w_i\),任意两点间都有边,边权为两端点权的异或值,求最小生成树边权和,以及方案数\(\bmod 10^9 + 7\) \(n \leq 1 ...

  2. cmake 学习-cmakelists.txt

    #设置库的路径,电脑里有qt4以及qt5,使用qt5时 设置qt5的环境变量(路径). set(CMAKE_PREFIX_PATH $ENV{QTDIR}) #设定工程名称 Project(prona ...

  3. go - GOPATH配置

    查看配置信息 go env 然后找到对应的 GOPATH=>修改为你自己的目录 ..liunx 系统下,注意要给权限 go bulid 编译文件 go run  先编译后执行. -------- ...

  4. nodejs基础(回调函数、模块、事件、文件读写、目录的创建与删除)

    node官网:http://nodejs.cn/ 今天想看看node的视频,对node进一步了解, 1.我们可以从官网下载node到自己的电脑上,今天了解到node的真正概念,node时javascr ...

  5. c 语言延时函数

    /*--- 等待x毫秒 ---*/ int sleep(unsigned long x) { clock_t c1 = clock(), c2; do { ) /* 错误 */ ; } while ( ...

  6. 基本PSO算法实现(Java)

    一.算法流程 Step1:初始化一群粒子(粒子个数为50个),包括随即位置和速度: Step2:计算每个粒子的适应度fitness: Step3:对每个粒子,将其适应度与其进过的最好位置(局部)pbe ...

  7. 响应式css样式

    <div class="a"> 123 </div> @media(orientation:portrait) and (max-width:600px){ ...

  8. Flutter移动电商实战 --(19)首页_火爆专区商品接口制作

    Dart中可选参数的设置 上节课在作通用方法的时候,我们的参数使用了一个必选参数,其实我们可以使用一个可选参数.Dart中的可选参数,直接使用“{}”(大括号)就可以了.可选参数在调用的时候必须使用p ...

  9. [go]json序列化

    // json字段别名 type User struct { Id int `json:"id,string"` Name string `json:"username& ...

  10. TortoiseSVN commit 停止工作

    TortoiseSVN commit 便停止工作,详细原因是igc64.dll故障,该动态链接库与Intel HD Graphics Driver有关(即显卡驱动),由于重装系统后,进行了显卡驱动的更 ...