同步工具类可以是任何一个对象,只要能够根据其自身的状态来协调线程的控制流就行,阻塞队列就可以作为同步工具类,java平台类库中还包含其他一些同步工具类,不过这里主要分享CountDownLatch、CyclicBarrier、Semaphore和 Exchanger。
CountDownLatch
CountDownLatch允许一个或多个线程等待其他线程完成操作。它维护了一个锁存器的计数,当计数为0的时候,那些处于wait状态的线程才能执行。 CountDownLatch是一种闭锁,闭锁相当于一扇门,在闭锁到达结束状态之前,门一直是关闭的,并且没有任何线程能通过,当到达结束状态时,门才会打开并允许所有的线程通过。
假如有这样一个需求:一个文本文件,有N行数据,通过多线程计算N行数据的和。可以通过CountDownLatch实现,先创建N个线程对每一行求和,最后主线程求整个文本文件的数据之和:
public class CountDownLatchExp {
private int [] nums;
public CountDownLatchExp(int lines){
nums = new int[lines];
}
public void calc (String line,int index,CountDownLatch latch){
String[] datas = line.split(",");
int total = 0;
for(String data:datas){
total += Integer.parseInt(data);
}
//每一行的和放到数组对应位置
nums[index] = total;
System.out.println(Thread.currentThread().getName()+"计算完成,结果为"+total);
latch.countDown(); //一行计算完就将锁存器减1
}
public static void main(String[] args) {
List<String> lines = readlines();//读文件
int lineCount = lines.size();
CountDownLatch latch = new CountDownLatch(lineCount);
CountDownLatchExp demo = new CountDownLatchExp(lineCount);
for(int i=0;i<lineCount;i++){
final int j = i;
new Thread(new Runnable() {
@Override
public void run() {
demo.calc(lines.get(j), j,latch);
}
}).start();
}
latch.await(); //当锁存器不为0就一直等待
demo.sum();//对数组求和
}
}
CyclicBarrier
CyclicBarrier的字面意思是可循环的屏障。它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻 塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续运行。也就是让一组线程到达 一个同步点后再一起继续运行
CyclicBarrier提供一个更高级的构造函数 CyclicBarrier(int parties, Runnable barrierAction) ,用于在线程到达同步点时,优先执行线程barrierAction,这样可以更加方便的处理一些复杂的业务场景, 且CyclicBarrier可用于多线程计算数据,最后合并计算结果的场景。
public class CalculateExp {
private int [] nums;
public CalculateExp(int lines){
nums = new int[lines];
}
public void calc (String line, int index, CyclicBarrier barrier){
String[] datas = line.split(",");
int total = 0;
for(String data:datas){
total += Integer.parseInt(data);
}
nums[index] = total;
System.out.println(Thread.currentThread().getName()+"计算完成,结果为"+total);
barrier.await();
}
public static void main(String[] args) {
List<String> lines = readlines();
int lineCount = lines.size();
CalculateExp demo = new CalculateExp(lineCount);
CyclicBarrier barrier = new CyclicBarrier(lineCount, new Runnable() {
@Override
public void run() {
demo.sum();//数组求和
}
});
for(int i=0;i<lineCount;i++){
final int j = i;
new Thread(new Runnable() {
@Override
public void run() {
demo.calc(lines.get(j), j,barrier);
}
}).start();
}
}
}
CyclicBarrier和CountDownLatch的区别:
CountDownLatch:一个或者多个线程,等待其他多个线程完成某件事情之后才能执行; CyclicBarrier:多个线程互相等待,直到到达同一个同步点,再继续一起执行。
CountDownLatch是计数器,只能使用一次,而CyclicBarrier的计数器提供reset功能 ,但使用reset的时候要 注意不要产生一直阻塞的线程。
Exchanger
Exchanger也是一种屏障,不过它是双方的(Two-Party),用于进行线程间的数据交换。它提供一个同步点,在这 个同步点,两个线程可以交换彼此的数据。这两个线程通过 exchange方法交换数据,如果第一个线程先执行 exchange()方法,它会一直等待第二个线程也执行exchange方法,当两个线程都到达同步点时,这两个线程就可以交换数据,将本线程生产出来的数据传递给对方。
Exchanger 使用是很简单,就提供了一个exchange操作和超时exchange操作,但实现原理不简单,底层也通过了 CAS算法实现线程安全。
Exchanger交换分为单槽和多槽,单个槽位在同一时刻只能用于两个线程交换数据, 再有线程进来竞争就实行多槽交换,多槽交换允许多个线程可以同时进行两两数据交换,彼此之间不受影响 ,且交换的对象是不确定的。Exchanger可用于结果校对,流水线设计,生产者消费者数据交换,遗传算法等。
//缓存交换
public class ExchangeCache {
private final static int MAX_COUNT = 10;
static class FillingLoop implements Runnable {
private Exchanger<ConcurrentHashMap<String,String>> exchanger;
ConcurrentHashMap<String,String> currentBuffer;
public FillingLoop(Exchanger<ConcurrentHashMap<String,String>>
exchanger,ConcurrentHashMap<String,String> currentBuffer){
this.exchanger = exchanger;
this.currentBuffer = currentBuffer;
}
public void run() {
int count = 0;
while (currentBuffer != null) {
if (currentBuffer.size() == MAX_COUNT)
currentBuffer = exchanger.exchange(currentBuffer);
Thread.sleep(500);
String key = ""+count;
String value = ""+count;
currentBuffer.put(key,value);
System.out.println("生产缓存:"+value); count++;
}
}
}
static class EmptyingLoop implements Runnable {
private Exchanger<ConcurrentHashMap<String,String>> exchanger;
ConcurrentHashMap<String,String> currentBuffer;
public EmptyingLoop(Exchanger<ConcurrentHashMap<String,String>>
exchanger,ConcurrentHashMap<String,String> currentBuffer){
this.exchanger = exchanger;
this.currentBuffer = currentBuffer;
}
public void run() {
int count = 0;
while (currentBuffer != null) {
if (currentBuffer.size() == 0)
currentBuffer = exchanger.exchange(currentBuffer);
Thread.sleep(500);
String key = ""+count;
System.out.println("消费缓存:"+currentBuffer.get(key)); currentBuffer.remove(key);
count++;
}
}
}
public static void main(String[] args) {
Exchanger<ConcurrentHashMap<String,String>> exchanger = new Exchanger<ConcurrentHashMap<String,String>>();
ConcurrentHashMap<String,String> currentBuffer1 = new ConcurrentHashMap<String,String>(MAX_COUNT);
ConcurrentHashMap<String,String> currentBuffer2 = new ConcurrentHashMap<String,String>(MAX_COUNT);
new Thread(new FillingLoop(exchanger,currentBuffer1)).start();
new Thread(new EmptyingLoop(exchanger,currentBuffer2)).start();
}
}
Semaphore
Semaphore(信号量)是用来控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共 资源。可以用于做流量控制等。通过 acquire() 获取一个许可,如果没有就等待,release() 释放一个许可。
public class SemaphoreExp {
public void method(Semaphore semaphore){
semaphore.acquire(); //进来
System.out.println(Thread.currentThread().getName()+" 执行。。。");
semaphore.release(); //释放
}
public static void main(String[] args) {
SemaphoreExp d = new SemaphoreExp();
Semaphore semaphore = new Semaphore(10);//method方法同时只能10个线程执行
int count = 0;
while(count<30){
new Thread(new Runnable() {
@Override
public void run() {
d.method(semaphore);
}
}).start();
count++;
}
}
}
本文深入探讨Java平台的四大并发工具类:CountDownLatch、CyclicBarrier、Semaphore和Exchanger的使用场景与实现原理,帮助理解多线程编程中的同步机制。
1493

被折叠的 条评论
为什么被折叠?



