1.3.4 并发工具类CountDownLatch/Semaphore/CyclicBarrier/FutureTask

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的更多相关文章
- Java中的并发工具类(CountDownLatch、CyclicBarrier、Semaphore、Exchanger)
在JDK的并发包里提供了很多有意思的并发工具类.CountDownLatch.CyclicBarrier和Semaphore 工具类提供了一种并发流程控制的手段,Exchanger 工具类则提供了在线 ...
- 25.大白话说java并发工具类-CountDownLatch,CyclicBarrier,Semaphore,Exchanger
1. 倒计时器CountDownLatch 在多线程协作完成业务功能时,有时候需要等待其他多个线程完成任务之后,主线程才能继续往下执行业务功能,在这种的业务场景下,通常可以使用Thread类的join ...
- java多线程10:并发工具类CountDownLatch、CyclicBarrier和Semaphore
在JDK的并发包(java.util.concurrent下)中给开发者提供了几个非常有用的并发工具类,让用户不需要再去关心如何在并发场景下写出同时兼顾线程安全性与高效率的代码. 本文分别介绍Coun ...
- 多线程学习笔记六之并发工具类CountDownLatch和CyclicBarrier
目录 简介 CountDownLatch 示例 实现分析 CountDownLatch与Thread.join() CyclicBarrier 实现分析 CountDownLatch和CyclicBa ...
- JAVA并发工具类---------------(CountDownLatch和CyclicBarrier)
CountDownLatch是什么 CountDownLatch,英文翻译为倒计时锁存器,是一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待. 闭锁可以延迟线程的进 ...
- Java 并发工具类 CountDownLatch、CyclicBarrier、Semaphore、Exchanger
本文部分摘自<Java 并发编程的艺术> CountDownLatch CountDownLatch 允许一个或多个线程等待其他线程完成操作.假设现有一个需求:我们需要解析一个 Excel ...
- Java中的4个并发工具类 CountDownLatch CyclicBarrier Semaphore Exchanger
在 java.util.concurrent 包中提供了 4 个有用的并发工具类 CountDownLatch 允许一个或多个线程等待其他线程完成操作,课题点 Thread 类的 join() 方法 ...
- Java并发工具类 - CountDownLatch
Java并发工具类 - CountDownLatch 1.简介 CountDownLatch是Java1.5之后引入的Java并发工具类,放在java.util.concurrent包下面 http: ...
- Java并发(十五):并发工具类——信号量Semaphore
先做总结: 1.Semaphore是什么? Semaphore(信号量)是用来控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源. 把它比作是控制流量的红绿灯,比如XX马路要 ...
随机推荐
- AtCoder Beginner Contest 128 F - Frog Jump
题意 有一只青蛙,有\(0, 1, \cdots, N - 1\)个荷叶.每个荷叶上有权值\(s_i\). 选定\(A\), \(B\),初始分数为\(0\). 当前位置为\(x\): 对于\(y = ...
- 异步时钟FIFO(一)
FIFO一般用于通过两个不同时钟域的数据传输.一个水池有进和出两个通道,由于进出口水流不一致所以需要水池加以缓冲.堆栈也是相当于水池的作用.如果输入端不是连续的数据流,可以通过堆栈来调节使数据以稳定的 ...
- 检查errno
转自 http://blog.csdn.net/todd911/article/details/9132095 很多库函数,特别是那些与操作系统有关的,当执行失败时会通过一个名称为errno的外部变量 ...
- NSString的导出
字符串的导出,写到某个文件中去 void stringExport(){ NSString *str=@"123456"; //if file not exist will not ...
- Python使用grequests并发发送请求
目录 前言 grequests简单使用 grequests和requests性能对比 异常处理 前言 requests是Python发送接口请求非常好用的一个三方库,由K神编写,简单,方便上手快.但是 ...
- flex的圣杯布局记录 (flex : 0 0 80px)
- JAVA基础知识|HTTP协议-两个特性
一.无连接 无连接:服务器与浏览器之间的一次连接只处理一个http请求,请求处理结束后,连接断开.下一次请求再重新建立连接. 然而随着互联网的发展,一台服务器同一时间处理的请求越来越多,如果依然采用原 ...
- nginx 记录
正则 ~ 区分大小写匹配 ~* 不区分大小写匹配 !~和!~*分别为区分大小写不匹配及不区分大小写不匹配 ^ 以什么开头的匹配 $ 以什么结尾的匹配 转义字符\ 可以转. * ?等 * 代表任意字符 ...
- 以太坊Geth通过私钥导入新地址到钱包步骤(3种方法)
一: 通过Geth客户端导入私钥: Open TextEdit Paste key into TextEdit without any extra characters or quotations S ...
- <JavaScript>可枚举属性与不可枚举属性
在JavaScript中,对象的属性分为可枚举和不可枚举之分,它们是由属性的enumerable值决定的.可枚举性决定了这个属性能否被for…in查找遍历到. 一.怎么判断属性是否可枚举 js中基本包 ...