并发编程之同步工具

本文深入探讨Java平台的四大并发工具类:CountDownLatch、CyclicBarrier、Semaphore和Exchanger的使用场景与实现原理,帮助理解多线程编程中的同步机制。

同步工具类可以是任何一个对象,只要能够根据其自身的状态来协调线程的控制流就行,阻塞队列就可以作为同步工具类,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++; 
		}
	} 
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值