同步工具类

闭锁


闭锁是一种同步工具类,可以延迟线程的进度直到其到达最终状态。闭锁的作用相当于一扇门:在闭锁到达结束状态之前,这扇门一直是关着的,并且没有任何线程能通过,

当到达结束状态时,这扇门会打开允许所有的线程通过。当闭锁到达结束状态后,将不会再改变状态,因此这扇门将永远保持打开状态。闭锁可以用来确保某些活动指导其他活动都完成后才继续执行,例如:

1、确保某个计算在其需要的所有资源都被初始化之后才继续执行。二元闭锁(包括两个状态)可以用来表示“资源R已经被初始化”,而所有需要R的操作都必须现在这个闭锁上等待。

2、确保某个服务在其依赖的所有其他服务都已经启动之后才启动,每个服务都有一个相关的二元闭锁。当启动服务S时,将首先在S依赖的其他服务的闭锁上等待,在所有依赖的服务都启动后悔释放闭锁S,这样其他依赖S的服务才能继续执行。

3、等待直到某个操作的所有参与者(例如:在多玩家游戏中的所有玩家)都就绪再继续执行。在这种情况直接哦欧诺个,当所有玩家都准备就绪时,闭锁将到达结束状态。


CountDownLatch是一种灵活的闭锁实现,可以在上述各种情况中使用。

闭锁状态包括一个计数器,该计数器被初始化为一个正数,表示需要等待的时间数量。countDown方法递减计数器,表示有一个时间已经发生了,而await方法等待计数器到达零,这表示所有需要等待的时间都已经发生。如果计数器的值非零,那么await会一直阻塞知道计数器为零,或者等待中的线程中断,或者等待超时。


下面的TestHarness中给出了闭锁的两种常见的用法。TestHarness创建一定数量的线程,利用它们并发地执行指定的任务。它使用两个闭锁,分别表示  起始门 和 结束门。起始门计数器的初始值为1,二结束们计数器的初始值为工作线程的数量。每个工作线程首先要做的值就是在启动门上等待,从而确保所有线程都就绪后才开始执行。而每个线程要做的最后一件事情就是将调用结束们的countDown方法减1,这能使主线程高效的等待直到所有工作线程都执行完成,因此可以统计所消耗的时间。


public class TestHarness {

	public long timeTasks(int nThreads, final Runnable task) throws InterruptedException{
		
		final CountDownLatch startGate =new CountDownLatch(1);
		final CountDownLatch endGate = new CountDownLatch(nThreads);
		
		for(int i = 0 ; i < nThreads ; i ++){
			
			Thread t = new Thread(){
				
				public void run(){
					
					try {
					
						startGate.await();

						try {
							task.run();
						}finally{
							endGate.countDown();
						}
						
					} catch (InterruptedException e) {

						e.printStackTrace();
					
					}
					
				}
				
			};
			t.start();
			
		}
		
		long start = System.nanoTime();
		startGate.countDown();
		endGate.await();
		long end = System.nanoTime();
		return end-start;
		
	}
	
}

FutureTask


FutureTask也可以用作闭锁。在Executor框架中表示异步任务,此外还可以用来表示一些时间较长的计算,这些计算可以在使用计算结果之前启动。


Semaphore


计数信号量(Count Semaphore) 用来控制同时访问某个特定资源的操作数量,或者同时执行某个指定操作的数量。计数信号量还可以用来实现某个资源池,或者对容器施加边界。

Semaphore中管理着一组虚拟的许可,许可的初始数量可通过构造函数来指定,在执行操作时可以首先获得许可,并在使用以后释放许可。如果没有许可那么acquire将阻塞直到有许可。release方法将返回一个许可给信号量,计算信号量的一种简化形式是二值信号量,即初始值为1的Semaphore。二值信号量可以用做互斥体,并具备不可重入的加锁语义:谁拥有这个唯一的许可,谁就拥有了互斥锁。

同样,你也可以使用Semaphore将任何一种容器变成有界阻塞容器,如下程序,信号量的计数值会初始化为容器容量的最大值。add操作在向底层容器中添加一个元素之前,首先要获取一个许可。如果add操作没有添加任何元素,那么会立刻释放许可。同样,remove操作释放一个许可,使更多的元素能够添加到容器中。底层的Set实现并不知道关于边界的任何信息,这是有BoundedHashSet来处理的。


public class BoundedHashSet<T> {

	private final Set<T> set;
	private final Semaphore sem;
	
	public BoundedHashSet(int bound){
		
		this.set = Collections.synchronizedSet(new HashSet<T>());
		sem = new Semaphore(bound);
		
	}
	
	public boolean add(T o) throws InterruptedException{
		
		sem.acquire();
		
		boolean wasAdded = false;
		
		try{
			wasAdded = set.add(o);
			return wasAdded;
		}finally{
			
			if (!wasAdded) {
				sem.release();
			}
			
		}
		
	}
	
	public boolean remove(Object o){
		boolean wasRemoved = set.remove(o);
		if (wasRemoved) {
			sem.release();
		}
		return wasRemoved;
	}
	
}


CyclicBarrie


栅栏(Barrier)类似于闭锁,它能阻塞一组线程直到某个事件发生。栅栏与闭锁的关键区别在于,所有线程必须同时到达栅栏位置,才能继续执行。闭锁用于等待事件,而栅栏用于等待其他线程。栅栏用于实现一些协议,例如几个家庭决定在某个地方集合:“所有人6:00在麦当劳碰头,到了以后要等待其他人,之后再讨论下一步要做的事情。”

CyclicBarrier可以使一定数量的参与方反复地在栅栏位置汇集,它在并行迭代算法中非常有用:这种算法通常将一个问题拆分成一系列相互独立的子问题。当线程到达栅栏位置时将调用await方法,这个方法将阻塞知道所有线程都到达栅栏位置。如果所有线程都到达了栅栏位置,那么栅栏将打开,此时所有线程都被释放,而栅栏将被重置以便下次使用。

如果成功地通过栅栏,那么await将为每个线程返回一个唯一的到达索引号,我们可以利用这些索引来选举产生一个领导线程,并在下一次迭代中由该领导线程执行一些特殊的工作。CyclicBarrier还可以使你将一个栅栏操作传递给构造函数,这是一个Runnable,当成功通过栅栏时会执行它,但在阻塞线程被释放之前是不能执行的。


public class CellularAutomata {

	private final CyclicBarrier barrier;
	public final Worker[] workers;
	
	public CellularAutomata(){
		
		int count = Runtime.getRuntime().availableProcessors();
		
		barrier = new CyclicBarrier(count, new Runnable() {
			
			public void run() {
				System.out.println("开始汇总");
			}
		});
		
		workers = new Worker[count];
		
		for(int i = 0 ; i < count ; i ++){
			
			workers[i] = new Worker("任务"+i,(i+1)*10000);
			
		}
		
	}
	
	private class Worker implements Runnable{

		private String task;
		private long time;
		
		public Worker(String task,long time){
			
			this.task = task;
			this.time = time;
		}
		
		public void run() {
			
			try {
				Thread.sleep(time);
			} catch (InterruptedException e1) {
				e1.printStackTrace();
			}
			System.out.println("已经完成"+task+"了");
			
			try {
				barrier.await();
			} catch (InterruptedException e) {
				e.printStackTrace();
			} catch (BrokenBarrierException e) {
				e.printStackTrace();
			}
			
		}
		
	}
	
	public static void main(String[] args) {
		
		CellularAutomata cl = new CellularAutomata();
		
		for(int i = 0 ; i < cl.workers.length ; i ++){
			
			new Thread(cl.workers[i]).start();
			
		}
		
	}
	
}






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值