Java各种Synchronizer: CountDownLatch, CyclicBarrier,Semaphore

首先,锁也是一种Synchronizer ,包括ReentrantLock, synchronized,是为了确保共享数据被完整一致的修改,不被破坏。


其他的,比如Object.wait(), notify()等 主要用于合作的线程之间彼此协调,Blocking Queue,FurtureTask 都是synchronizer


CountDownLatch

有两个方法await() 和countDown(),用于主线程调用latch.await(),每个子线程完整自己的工作后调用latch.countDown(),当所有的子线程都表示down了,即latch的计数为0了, 主线程才从await返回往下执行。 类似Thread.join(),但是不需要针对Thread对象本身操作,而且,子线程done了不一定结束,调了latch.countDown()后还可以继续干别的事。 

初始计数值的大小,对应子任务的数目,而不一定是子线程的数目,可以一个子线程对应一个task,也可以多个


CyclicBarrier 栅栏

CyclicBarrier只有一个方法await(),好比一伙人到一个地方活动,先到的人先await(), 等所有的人都到了,才开始下一步活动。有几个人初始值就设为几,当所有的线程都到达barrier.await()这个点,大家都从await()中唤醒往下执行,最后一个调barrier.await的线程其实不阻塞。CyclicBarrier的构造函数还可以指定一个Runable,表示大家都到齐之后可以执行一个活动,这个是CyclicBarrier内部新开一个线程去执行的。

import java.util.Random;  
import java.util.concurrent.BrokenBarrierException;  
import java.util.concurrent.CyclicBarrier;  
import java.util.concurrent.ExecutorService;  
import java.util.concurrent.Executors;  
  
public class TestCyclicBarrier {  
  
    public static void main(String[] args) {  
      
        ExecutorService exec = Executors.newCachedThreadPool();       
        final Random random=new Random();  
          
        final CyclicBarrier barrier=new CyclicBarrier(4,new Runnable(){  
            @Override  
            public void run() {  
                System.out.println("大家都到齐了,开始happy去");  
            }});  
          
        for(int i=0;i<4;i++){  
            exec.execute(new Runnable(){  
                @Override  
                public void run() {  
                    try {  
                        Thread.sleep(random.nextInt(1000));  
                    } catch (InterruptedException e) {  
                        e.printStackTrace();  
                    }  
                    System.out.println(Thread.currentThread().getName()+"到了,其他哥们呢");  
                    try {  
                        barrier.await();//等待其他哥们  
                    } catch (InterruptedException e) {  
                        e.printStackTrace();  
                    } catch (BrokenBarrierException e) {  
                        e.printStackTrace();  
                    }  
                }});  
        }  
        exec.shutdown();  
    }  
}  


Semaphore

是独占锁的generalization版本,允许一定数量的线程进入而不是一个。


Object.wait() / notifyAll(),  Condition.await() / signalAll(),基于条件的Synchronizer 

之前的Synchronizer 是基于独占(lock),限流(Semaphore),或者某种计数(CountDownLatch,CylicBarrier),而这组Synchronizer 是基于条件的

有的线程等待某种条件为真才往下执行,有的线程改变这个条件。


基于条件的Synchronizer (wait, notify) 是最基本的,其他的synchronizer都可以用wait / notify自己实现,只是不同synchronizer的条件不一样而已

CountDownLatch 和CyclicBarrier 的条件都是一个计数值,

对于CountDownLatch , 一部分线程调用await等待计数值为0,另一部分线程调用coutDown的线程改变计数值

对于CyclicBarrier 只有await操作,所有线程都调用await,awai操作t既改变计数值,又会等待计数值为0

class CountDownLatch {
	int count;
	Lock lock;
	Condition c;
	public CountDownLatch(int x) {
		count = x;
		lock = new ReentrantLock();
		c = lock.newCondition();
	}
	public void countDown() {
		lock.lock();
		try {
			if (count > 0) --count;
			if (count == 0) c.signalAll();
		} finally {
			lock.unlock();
		}
	}	
	public void await() {
		lock.lock();
		try {
			if (count > 0) c.await();
		} catch (InterruptedException e){
			
		}
		finally {
			lock.unlock();
		}
	}
}

一种有界阻塞队列的实现,条件是queue的 size, 也可以用Semaphore做

class BoundedQueue<T> {
	private Deque<T> q = new LinkedList<T>();
	private int size;
	private Lock lock = new ReentrantLock();
	private Condition produceSignal;
	private Condition consumeSignal;
	public BoundedQueue(int size) {
		this.size = size;
		produceSignal = lock.newCondition();
		consumeSignal = lock.newCondition();
	}
	
	public  boolean offer(T e) throws InterruptedException {
		try {
			lock.lock();
			while(q.size() == size) {
				produceSignal.await();				
			}
			if (q.offer(e)) {
				consumeSignal.signal();
				return true;
			}
			return false;
		} finally {
			lock.unlock();
		}
		
		
	}
	public T poll() throws InterruptedException {
		try {
			lock.lock();
			while (q.isEmpty()) {
				consumeSignal.await();				
			}
			return q.poll();
		} finally {
			lock.unlock();
		}
	}
}
class BoundedQueue2<T> {
	Deque<T> queue = new LinkedList<T>();
	Semaphore emptySem, fullSem;
	public BoundedQueue2(int size) {
		emptySem = new Semaphore(size);
		fullSem = new Semaphore(0);
	}
	public void offer(T item) throws InterruptedException {
		emptySem.acquire();
		if (queue.offer(item))
			fullSem.release();
		else
			emptySem.release();
	}
	
	public T poll() throws InterruptedException {
		fullSem.acquire();
		T ret = queue.poll();
		emptySem.release();
		return ret;
	}
}

比较一下两种实现,1)锁+ 自定义 条件(size)  , 2)2个Semaphone,

1)Semaphore把计数的check 和修改在一起作为原子操作, 而方案一需要自己维护lock保护共享数据,并且需要while来循环检查条件,多浪费了些cpu资源

2)Semaphore的方案并发度更细,并没有额外的去保护内置队列同时只被一个线程访问,这个是否有必要,看情景多线程读多线程写的情况是需要保护内置结构的访问的,比如连续的两个写,从Semaphore角度只要没满都可以过,但是同时的插入队尾是有问题的。

3)总结,Semaphore提供的便利类似原子操作CAS,比较和Increment一起作为原子操作。只是确保有资源的情况才允许线程进入,但是没有解决另一个问题,共享数据被安全的访问的问题。




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值