同步工具类(闭锁,信号量,FutureTask,信号量,栅栏)

同步工具类可以使任何一个对象,只要它根据自身的状态来协调线程的控制流。阻塞队列可以作为同步工具类,其他类型的同步工具类还包括信号量,栅栏以及闭锁。
所有的同步工具类都包含一些特定的结构化属性:封装了一些状态,这些状态将决定执行同步工具类的线程是继续执行还是等待,还提供一些方法对状态进行操作,自己用于高效的等待同步工具类进入预期状态的方法。

闭锁

可以延迟线程的进度直到其到达终止状态。
闭锁作用相当于一扇门:到达结束状态前,门一直关闭,任何线程不得通过执行,只有,闭锁这个门打开,才会允许其他线程执行。状态只能从关闭到打开,并一直保持。闭锁可以确保某些活动直到其他活动都完成才继续执行:
eg:

  • 确保计算的资源都被初始化才执行计算。
  • 确保每个服务所依赖的服务启动后才启动。
  • 等待直到某个操作的所有参与者(例如,多玩家游戏中的所有玩家)都就绪再继续执行。

CountDown时灵活的闭锁实现,可以使一个或多个线程等待一组事件的发生。包含一个计数器,通过构造函数初始化,调用countDown(),计数器每次减1,表示有一个事件发生了,await()方法等待计数器到达零,表示所有等待事件均已发生,如果非零,await()会一直阻塞,直到零,或者等待线程中断,或超时。
示例如下:线程创建后并不会立即执行,而是在startGate.countDown()之后执行,保证了所有创建的线程一起执行,而不是一些还没创建,一些已经在执行。启动门使得同一时间释放所有线程,结束们保证所有线程结束才执行下面操作。

public long timeTasks(int nThreads, final Runnable task){
		final CountDownLatch startGate = new CountDownLatch(1);
		final CountDownLatch endGate = new CountDownLatch(nThreads);
		for( int i = 0; i < nThreads; i++){
			Thread  t = new Thread(i + ""){
				@Override
				public void run(){
					try {
						System.out.println("阻塞等待事件" + Thread.currentThread().getName() );
						startGate.await();
						try {
							task.run();
						}finally {
							endGate.countDown();
						}
					}catch (Exception e){
						e.printStackTrace();
					}
				}
			};
			t.start();
		}

		long start = System.nanoTime();
		System.out.println("主线程执行");
		try {
			Thread.sleep(10);
			//让主线程等一下看事件组会执行吗
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		startGate.countDown();
		System.out.println("等待事件组可以执行");
		try {
			System.out.println("等待事件组未执行完毕");
			endGate.await();
			System.out.println("等待事件组执行完毕");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		long end = System.nanoTime();
		return end - start;


	}

FutureTask

FutureTask也可以做闭锁(实现了Future的语义,表示一种抽象的可计算的结果)。通过Callable实现,相当于一个可生成结果的Runnable.
三种状态:

  • 等待运行
  • 正在运行
  • 运行完成
    “运行完成”表示计算的所有可能结束的状态,包含正常结束,由于取消而结束和由于异常而结束。当进入完成状态,他会停止在这个状态上。
    Furture.get()获取执行结果的值,取决于执行的状态,如果任务完成,会立即返回结果,否则一直阻塞直到任务进入完成状态,然后返回结果或者抛出异常。FutureTask负责将计算结果从执行任务的线程传递到调用这个线程的线程,而且确保了 传递过程中结果的安全发布
public class PreLoader {
	private final FutureTask<Product> future = new FutureTask<Product>(new Callable<Product>(){
		public Product call() throws Exception{
			//执行一些耗时任务
			return loadProduct();
		}
	});

	private final Thread thread = new Thread(future);

	public void start(){
		thread.start();
	}

	private Product loadProduct() {
		Product product = new Product();
		//可从数据库或者其他方式获取,耗时任务
		return product;
	}

	public Product get(){
		try {
			return  future.get();
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (ExecutionException e) {
			e.printStackTrace();
		}
		return null;
	}

}

信号量

用来控制同时访问某个特定资源的操作数量,或者同时执行某个指定操作的数量,还可以实现对某种资源池或者同期划分边界
Semaphore管理一组许可,通过构造函数设置。执行操作时需要先获得许可(有剩余)通过acquire()方法尝试获取许可,如果没有许可,会一直阻塞直到有许可(中断或超时)。release()方法将释放一个许可给信号量。二值信号量可以做互斥体,并具备不可重入锁语义,可以做互斥锁。
Semaphore可以用于资源池,比如数据库连接池,可构造固定长度数据库连接池,当池为空则请求失败(实际为阻塞)还可以将容易变为有界阻塞容器 如下例:

public class TestSemaphore {
	private final Set<T> set;
	private final Semaphore sem;

	public TestSemaphore(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 {
			//如果add操作不成功,立刻释放许可
			if(!wasAdded){
				sem.release();
			}
		}
		
		
	}
	
	public boolean remove(Object o){
		boolean wasRemoved = set.remove(o);
		
		//删除成功,释放许可
		if(wasRemoved){
			sem.release();
		}
		return wasRemoved;
	}
	
	
	
}

栅栏(Barrier)

CyclicBarrier

栅栏(Barrier)类似于闭锁,它能阻塞一组线程直到某个事件的发生。栅栏与闭锁的区别在于,所有的线程必须同时到达栅栏未知,才能继续执行。闭锁用于等待事件,而栅栏用于等待其他线程。
CyclicBarrier可以使一定数量的参与方反复的在栅栏处等待汇集。在迭代算法中非常有用:这种算法通常将一个问题拆分成一系列相互独立的子问题。当线程到达栅栏将调用await()方法,这个方法将阻塞,直到所有线程到达栅栏处,如果所有线程达到了栅栏处,栅栏将打开,此时所有线程释放,而栅栏将被重置,以便下次使用。如果对await()方法调用超时,或者await阻塞的线程被中断,那么栅栏被认为是打破,所有await()调用将终止并抛出BrokenBarrierException,如果成功通过栅栏,那么await()将为每个线程返回一个唯一索引号。
CyclicBarrier存在两个构造函数

public CyclicBarrier(int parties);
public CyclicBarrier(int parties, Runnable barrierAction);
//第一个参数表示等待的线程数,第二个表示所有线程结束执行的操作。

第二个参数是一个Runnable,当成功通过栅栏时(在子任务线程)执行它,但在阻塞线程被释放之前是不能执行的。
另一个栅栏是EXchanger

Exchanger

**它是在两个任务之间交换对象的栅栏。当这些任务进入栅栏时,各持有一个对象,当他们离开时,他们都拥有之前由对象持有的对象。典型应用场景:
一个任务在创建对象,这些对象的生产代价很高昂,而另一个任务消费这些对象。通过这种方式,可以有更多的对象在被创建的同事被消费。

代码示例详见github

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值