一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。
用给定的计数 初始化 CountDownLatch。由于调用了 countDown() 方法,所以在当前计数到达零之前,await 方法会一直受阻塞。之后,会释放所有等待的线程,await 的所有后续调用都将立即返回。这种现象只出现一次——计数无法被重置。如果需要重置计数,请考虑使用 CyclicBarrier。
CountDownLatch 是一个通用同步工具,它有很多用途。将计数 1 初始化的 CountDownLatch 用作一个简单的开/关锁存器,或入口:在通过调用 countDown() 的线程打开入口前,所有调用 await 的线程都一直在入口处等待。用 N 初始化的 CountDownLatch 可以使一个线程在 N 个线程完成某项操作之前一直等待,或者使其在某项操作完成 N 次之前一直等待。
CountDownLatch 的一个有用特性是,它不要求调用 countDown 方法的线程等到计数到达零时才继续,而在所有线程都能通过之前,它只是阻止任何线程继续通过一个 await。
nums.txt文本文件
10,20,30,33,12,23
21,12,18,45,11
23,45,67,78,89
12,12
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class Demo {
private int[] nums;
public Demo(int line) {
nums = new int[line];
}
public void calc(String line, int index) {
String[] nus = line.split(","); // 切分出每个值
int total = 0;
for (String num : nus) {
total += Integer.parseInt(num);
}
nums[index] = total; // 把计算的结果放到数组中指定的位置
System.out.println(Thread.currentThread().getName() + " 执行计算任务... " + line + " 结果为:" + total);
}
public void sum() {
System.out.println("汇总线程开始执行... ");
int total = 0;
for (int i = 0; i < nums.length; i++) {
total += nums[i];
}
System.out.println("最终的结果为:" + total);
}
public static void main(String[] args) {
List contents = readFile();
int lineCount = contents.size();
Demo d = new Demo(lineCount);
for (int i = 0; i < lineCount; i++) {
final int j = i;
new Thread(new Runnable() {
@Override
public void run() {
d.calc(contents.get(j), j);
}
}).start();
}
while (Thread.activeCount() > 1) {
}
d.sum();
}
private static List readFile() {
List contents = new ArrayList<>();
String line = null;
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader("d:\\nums.txt"));
while ((line = br.readLine()) != null) {
contents.add(line);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
return contents;
}
用CountDownLatch之后
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
public class Demo2 {
private int[] nums;
public Demo2(int line) {
nums = new int[line];
}
public void calc(String line, int index, CountDownLatch latch) {
String[] nus = line.split(","); // 切分出每个值
int total = 0;
for (String num : nus) {
total += Integer.parseInt(num);
}
nums[index] = total; // 把计算的结果放到数组中指定的位置
System.out.println(Thread.currentThread().getName() + " 执行计算任务... " + line + " 结果为:" + total);
latch.countDown();
}
public void sum() {
System.out.println("汇总线程开始执行... ");
int total = 0;
for (int i = 0; i < nums.length; i++) {
total += nums[i];
}
System.out.println("最终的结果为:" + total);
}
public static void main(String[] args) {
List contents = readFile();
int lineCount = contents.size();
CountDownLatch latch = new CountDownLatch(lineCount);
Demo2 d = new Demo2(lineCount);
for (int i = 0; i < lineCount; i++) {
final int j = i;
new Thread(new Runnable() {
@Override
public void run() {
d.calc(contents.get(j), j, latch);
}
}).start();
}
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
d.sum();
}
private static List readFile() {
List contents = new ArrayList<>();
String line = null;
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader("d:\\nums.txt"));
while ((line = br.readLine()) != null) {
contents.add(line);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
return contents;
}
}
这里其实就是把传统的做法变化了一下、CountDownLatch利用一个计数器来完成,这里面最主要的方法就是上面图片中所标识的2个方法、在上述实例中先完成行数的统计传入CountDownLatch,在线程每完成一个就调用CountDownLatch的countDown方法来将数量减一、达到零时,则释放所有等待线程。
CountDownLatch是继承了AbstractQueuedSynchronizer类,一个共享锁的实现
上图的await方法就是判断getState的值是否等于0等于0执行不等于0返回-1所以不执行
这里countDown就是值减一,而我们总行数减到0之后所有线程便执行了
一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时 CyclicBarrier 很有用。因为该 barrier 在释放等待线程后可以重用,所以称它为循环 的 barrier。
CyclicBarrier 支持一个可选的 Runnable 命令,在一组线程中的最后一个线程到达之后(但在释放所有线程之前),该命令只在每个屏障点运行一次。若在继续所有参与线程之前更新共享状态,此屏障操作 很有用。
如:一个开会的例子、不管你什么时候到的,一定要等到规定人数到了之后才进行、否则就一直等待,直到人数达到规定数量才继续向下执行
import java.util.Random;
import java.util.concurrent.CyclicBarrier;
public class Demo {
Random random = new Random();
public void meeting(CyclicBarrier barrier) {
try {
Thread.sleep(random.nextInt(4000));
} catch (InterruptedException e1) {
e1.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 到达会议室,等待开会..");
try {
barrier.await();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Demo demo = new Demo();
CyclicBarrier barrier = new CyclicBarrier(10, new Runnable() {
@Override
public void run() {
System.out.println("好!我们开始开会...");
}
});
for (int i = 0; i < 10; i++) {
new Thread(new Runnable() {
@Override
public void run() {
demo.meeting(barrier);
}
}).start();
}
}
}
这里的CyclicBarrier其实和CountDownLatch差不多、这里还传入了一个事件,就是当条件达到的时候需要做什么事
这个的话其实我们就是调用await方法、而await方法就调用了dowait方法,这个dowait方法就是整个CyclicBarrier类的核心
private int dowait(boolean timed, long nanos)
throws InterruptedException, BrokenBarrierException,
TimeoutException {
final ReentrantLock lock = this.lock;
lock.lock();
try {
final Generation g = generation;
//通过Generation的broken方法了判断是否中断,如果中断了,抛出一个中断的异常
if (g.broken)
throw new BrokenBarrierException();
//判断这个线程有没有被中断过
if (Thread.interrupted()) {
breakBarrier();
throw new InterruptedException();
}
//进来一个线程,count就递减,说明等待的线程就少一个
int index = --count;
if (index == 0) { // tripped
//当index等待线程为0则叫醒所有的等待线程
boolean ranAction = false;
try {
//在判断barrierAction是否有事件传入,有就执行
final Runnable command = barrierCommand;
if (command != null)
command.run();
ranAction = true;
nextGeneration();
return 0;
} finally {
//如果执行事件报错则打破屏障叫醒所有线程
if (!ranAction)
breakBarrier();
}
}
//后面就是不为0的处理判断是否重置超时什么的
// loop until tripped, broken, interrupted, or timed out
for (;;) {
try {
if (!timed)
trip.await();
else if (nanos > 0L)
nanos = trip.awaitNanos(nanos);
} catch (InterruptedException ie) {
if (g == generation && ! g.broken) {
breakBarrier();
throw ie;
} else {
// We're about to finish waiting even if we had not
// been interrupted, so this interrupt is deemed to
// "belong" to subsequent execution.
Thread.currentThread().interrupt();
}
}
if (g.broken)
throw new BrokenBarrierException();
if (g != generation)
return index;
if (timed && nanos <= 0L) {
breakBarrier();
throw new TimeoutException();
}
}
} finally {
lock.unlock();
}
}
这里的话引用了ReentrantLock进行加锁,来保证线程是安全的,而Generation就是为了实现reset重置功能的
一个计数信号量。从概念上讲,信号量维护了一个许可集。如有必要,在许可可用前会阻塞每一个 acquire(),然后再获取该许可。每个 release() 添加一个许可,从而可能释放一个正在阻塞的获取者。但是,不使用实际的许可对象,Semaphore 只对可用许可的号码进行计数,并采取相应的行动。
Semaphore 通常用于限制可以访问某些资源(物理或逻辑的)的线程数目。
import java.util.concurrent.Semaphore;
public class Demo {
public void method (Semaphore semaphore) {
try {
semaphore.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " is run ...");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
semaphore.release();
}
public static void main(String[] args) {
Demo d = new Demo();
Semaphore semaphore = new Semaphore(10);
while(true) {
new Thread(new Runnable() {
@Override
public void run() {
d.method(semaphore);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}).start();
}
}
}
每次只也许n个线程执行、其余的线程将被等待在那不释放也不有些、直到前面n个线程有一个运行完毕后释放资源后、其余线程去竞争运行。与线程池的差不多,只不过Semaphore会创建线程等待在那、而线程池不会创建线程、都是规定只能有n个线程执行。
可以在对中对元素进行配对和交换的线程的同步点。每个线程将条目上的某个方法呈现给 exchange 方法,与伙伴线程进行匹配,并且在返回时接收其伙伴的对象。Exchanger 可能被视为 SynchronousQueue 的双向形式。Exchanger 可能在应用程序(比如遗传算法和管道设计)中很有用。
import java.util.concurrent.Exchanger;
public class Demo {
public void a (Exchanger exch) {
System.out.println("a 方法执行...");
try {
System.out.println("a 线程正在抓取数据...");
Thread.sleep(2000);
System.out.println("a 线程抓取到数据...");
} catch (InterruptedException e) {
e.printStackTrace();
}
String res = "12345";
try {
System.out.println("a 等待对比结果... 抓取数据为:" + res);
String value = exch.exchange(res);
System.out.println("a获取b交换过来的数据:" + value);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void b (Exchanger exch) {
System.out.println("b 方法开始执行...");
try {
System.out.println("b 方法开始抓取数据...");
Thread.sleep(4000);
System.out.println("b 方法抓取数据结束...");
} catch (InterruptedException e) {
e.printStackTrace();
}
String res = "1234567";
try {
String value = exch.exchange(res);
System.out.println("开始进行比对...");
System.out.println("b获取a交换过来的数据:" + value);
System.out.println("比对结果为:" + value.equals(res));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Demo d = new Demo();
Exchanger exch = new Exchanger<>();
new Thread(new Runnable() {
@Override
public void run() {
d.a(exch);
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
d.b(exch);
}
}).start();
}
}
大家可以发现,其实Exchanger作用就是线程与线程之间的数据交换