同步器Synchronizer

1.同步器Synchronizer
synchronizer是一个对象,它根据自身状态调节线程的控制流。阻塞队列可以扮演一个synchronizer,其他类型的synchronizer包括:信号量(semaphore)、关卡(barrier)和闭锁(latch)。
1.1闭锁(latch)
闭锁是可以延迟线程的进度直到线程到达终点状态的一种同步器。每个闭锁就相当于是一道大门,在闭锁到达重点状态之前,门一直是关闭的,没有线程可以通过,当到达终点状态时,门打开了,允许所有的线程通过。一旦闭锁到达终点状态,就不能再改变状态了,也就是永远保持开放的状态。闭锁用来确保特定活动直到其他活动都完成后才发生。
/**  
* @Title: TestHarnessTest.java  
* @Package synchronizer  
* @Description: TODO  
* @author 落叶飞翔的蜗牛  
* @date 2017年12月16日 上午10:40:11
* @version V1.0  
*/
package synchronizer;


import java.util.concurrent.CountDownLatch;


/**
 * @author 落叶飞翔的蜗牛
 * @date 2017年12月16日 上午10:40:11
 */
public class TestHarness {
	public static long timeTasks(int nThread, final Runnable task) {
		long start = System.currentTimeMillis();


		final CountDownLatch startGate = new CountDownLatch(1);
		final CountDownLatch endGate = new CountDownLatch(nThread);


		for (int i = 0; i < nThread; i++) {
			Thread thread = new Thread() {
				@Override
				public void run() {
					try {
						startGate.await();
						try {
							task.run();
						} finally {
							System.out.println("单个任务结束");
							endGate.countDown();
						}
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			};
			thread.start();
		}


		startGate.countDown();
		try {
			endGate.await();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("所有任务结束");
		long end = System.currentTimeMillis();
		return end - start;
	}
}

/**  
* @Title: TestHarnessTest.java  
* @Package synchronizer  
* @Description: TODO  
* @author 落叶飞翔的蜗牛  
* @date 2017年12月16日 上午10:46:05
* @version V1.0  
*/  
package synchronizer;


import org.junit.Test;


/**
 * @author 落叶飞翔的蜗牛
 * @date 2017年12月16日 上午10:46:05
 */
public class TestHarnessTest {
	@Test
    public void test() {
        int nThread = 2;


        Runnable task = new Runnable() {
            @Override
            public void run() {
                System.out.println("单个任务开始");
                try {
                    Thread.sleep(1000*5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };


        long time = TestHarness.timeTasks(nThread, task);
        System.out.println("运行耗时:" + time);
    }
}


TestHarness使用两个阀门,开始阀门将计数器初始化为1,结束阀门将计数器初始化为工作线程的数量。每个工作线程要做的第一件事是等待阀门打开。每个线程最后一件事是将结束阀门减一,这样做使得工作线程有效的等待,直到最后一个工作线程完成了自己的任务,这个就可以计算整个过程的耗时。
1.2 FutureTask
FutureTask也可以作为闭锁。FutureTask描述了一个抽象的可携带结果的计算。FutureTask的计算是通过Callable实现的,它等价于一个可以携带结果的Runnable。它有三个状态:等待、运行和完成。完成包括:正常结束、取消和异常结束。一旦FutureTask进入完成状态,他就会永远停止在这个状态上。
Future.get的行为依赖于任务的状态。如果它已经完成,get立刻返回得到结果,否则会阻塞直到任务进入完成状态。FutureTask把计算的结果从当前的线程传送到需要该结果的线程。
利用FutureTask来完成异步任务,并在真正需要的计算结果之前就启动异步任务,这样可以减少等待的时间。
/**  
* @Title: PreLoader.java  
* @Package synchronizer.future  
* @Description: TODO  
* @author 落叶飞翔的蜗牛  
* @date 2017年12月16日 下午2:24:46
* @version V1.0  
*/
package synchronizer.future;


import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;


/**
 * @author 落叶飞翔的蜗牛
 * @date 2017年12月16日 下午2:24:46
 */
public class PreLoader {
	
	/**
	 * future:FutureTask<Object> TODO(模拟异步任务).
	 * @author: 落叶飞翔的蜗牛
	 * @date: 2017年12月16日 下午3:17:41
	 */
	private final FutureTask<Object> future = new FutureTask<>(new Callable<Object>() {


		/**
		 * TODO 异步任务,睡眠5s
		 * @see java.util.concurrent.Callable#call()
		 */
		@Override
		public Object call() throws Exception {
			Thread.sleep(5000);
			return "SUCCESS";
		}
	});


	private final Thread thread = new Thread(future);


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


	/**
	 * get:(获取异步线程执行结果). <br/>
	 * @author 落叶飞翔的蜗牛
	 * @date:2017年12月16日 下午3:24:52
	 * @return Object
	 */
	public Object get() {
		try {
			return future.get();
		} catch (InterruptedException | ExecutionException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}
}

/**    
* @Title: PreLoaderTest.java  
* @Package synchronizer.future  
* @Description: TODO(用一句话描述该文件做什么)  
* @author 落叶飞翔的蜗牛
* @date 2017年12月16日 下午3:34:56  
* @version V1.0    
*/
package synchronizer.future;


import org.junit.Test;


/**
 * ClassName: PreLoaderTest <br/>
 * Function: 测试PreLoader. <br/>
 * date: 2017年12月16日 下午3:34:56 <br/>
 * @author 落叶飞翔的蜗牛
 * @version 
 * @since JDK 1.6
 */
public class PreLoaderTest {
	
	/**
	 * test:(测试PreLoader). <br/>
	 * @author 落叶飞翔的蜗牛
	 * @date:2017年12月16日 下午3:47:29 
	 */
	@Test
	public void test() {
		PreLoader preLoader = new PreLoader();
		preLoader.start();
		long start = System.currentTimeMillis();
		/**
		 * 当程序需要用到异步数据时,可以调用get方法.
		 * 如果异步任务完成,这直接返回.
		 * 否则等待加载结束后返回
		 */
		System.out.println("获取结果:" + preLoader.get());
		long end = System.currentTimeMillis();
		System.out.println("执行耗时: " + (end - start));
	}
}

1.3 信号量(semaphore)
信号量用来控制能够同时访问某特定资源的活动的数量,或者是同时执行某一给定操作的数量。信号量可以用来实现资源池或者某一容器的限定的边界。
一个信号量管理一个有效的许可集合,活动能够获得许可,并在使用完后,释放许可。如果没可用的许可了,那么acquire方法会被阻塞,只有有可用的许可为止。Release方法向信号量返回一个许可。
信号量可以用来实现资源池,比如数据库连接池。每个池子有固定的长度,当池子为空,向池子申请资源将会失败,需要阻塞请求,一旦池子非空,解除阻塞。
用信号量将容器转化为有界容器。信号量被初始化为指定的大小,每次添加操作,向底层容器执行add操作之前,获取一个许可。同样的,每次删除的时候,在底层容器删除之后,释放一个许可。
/**    
* @Title: BoundedHashSet.java  
* @Package synchronizer.semaphore  
* @Description: TODO(有界HashSet)  
* @author 落叶飞翔的蜗牛
* @date 2017年12月16日 下午4:56:20  
* @version V1.0    
*/
package synchronizer.semaphore;


import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.Semaphore;


/**
 * ClassName: BoundedHashSet <br/>
 * @author 落叶飞翔的蜗牛
 * date: 2017年12月16日 下午4:56:20 <br/>
 * Function: TODO 有界HashSet. <br/>
 * Reason: TODO ADD REASON(可选). <br/>
 * @version V1.0
 */
public class BoundedHashSet<T> {
	private Set<T> set = null;
	
	private Semaphore semaphore = null;
	
	public BoundedHashSet(int bound) {
		this.set = Collections.synchronizedSet(new HashSet<>());
		semaphore = new Semaphore(bound);
	}
	
	/**
	 * add:(添加元素). <br/>
	 * TODO (添加元素之前,先获取一个许可,如果没有剩余许可,阻塞).<br/>
	 * @author 落叶飞翔的蜗牛
	 * @date: 2017年12月16日 下午5:11:14
	 * @param t
	 * @return
	 * @throws InterruptedException 
	 */
	public boolean add(T t) throws InterruptedException {
		semaphore.acquire();
		boolean isAdded = false;
		try {
			isAdded = set.add(t);
			return isAdded;
		} finally {
			if(!isAdded) {
				semaphore.release();
			}
		}
	}
	
	/**
	 * remove:(删除操作). <br/>
	 * TODO (删除成功后释放许可).<br/>
	 * @author 落叶飞翔的蜗牛
	 * @date: 2017年12月16日 下午5:14:35
	 * @param t
	 * @return 
	 */
	public boolean remove(T t) {
		boolean isRemoved = set.remove(t);
		if(isRemoved) {
			semaphore.release();
		}
		return isRemoved;
	}
}

/**    
* @Title: BoundedHashSetTest.java  
* @Package synchronizer.semaphore  
* @Description: 测试BoundedHashSet  
* @author 落叶飞翔的蜗牛
* @date 2017年12月16日 下午5:18:12  
* @version V1.0    
*/
package synchronizer.semaphore;


import org.junit.Test;


/**  
* @ClassName: BoundedHashSetTest  
* @Description: (测试BoundedHashSet)  
* @author 落叶飞翔的蜗牛 
* @date 2017年12月16日 下午5:18:12    
*/
public class BoundedHashSetTest {
	
	/**
	 * testAdd:(测试添加). <br/>
	 * @Description: (第四个元素加入失败).<br/>
	 * @author 落叶飞翔的蜗牛
	 * @date: 2017年12月16日 下午5:27:06
	 * @throws InterruptedException 
	 */
	@Test
	public void testAdd() throws InterruptedException {
		BoundedHashSet<String> boundedHashSet = new BoundedHashSet<>(3);
		boundedHashSet.add("1");
		System.out.println("加入第1个元素成功");
		boundedHashSet.add("2");
		System.out.println("加入第2个元素成功");
		boundedHashSet.add("3");
		System.out.println("加入第3个元素成功");
		boundedHashSet.add("4");
		System.out.println("加入第4个元素成功");
	}
	
	/**
	 * testAddAndRemove:(测试添加删除). <br/>
	 * @Description: (删除元素1后,元素4就可以加入).<br/>
	 * @author 落叶飞翔的蜗牛
	 * @date: 2017年12月16日 下午5:29:45
	 * @throws InterruptedException 
	 */
	@Test
	public void testAddAndRemove() throws InterruptedException {
		BoundedHashSet<String> boundedHashSet = new BoundedHashSet<>(3);
		boundedHashSet.add("1");
		System.out.println("加入第1个元素成功");
		boundedHashSet.add("2");
		System.out.println("加入第2个元素成功");
		boundedHashSet.add("3");
		System.out.println("加入第3个元素成功");
		boundedHashSet.remove("1");
		System.out.println("删除第1个元素成功");
		boundedHashSet.add("4");
		System.out.println("加入第4个元素成功");
	}
}

1.4 关卡(barrier)
关卡类似于闭锁,它能阻塞一组线程,知道某些事件的发生。关卡与闭锁的关键不同在于,所有的线程必须同时到达关卡点,才能继续处理。闭锁等待的是事件,关卡等待的是其他线程。就是说闭锁用来等待的事件就是countDown事件,只有该countDown事件执行后所有之前在等待的线程才有可能继续执行;而栅栏没有类似countDown事件控制线程的执行,只有线程的await方法能控制等待的线程执行。CyclicBarrier强调的是n个线程,大家相互等待,只要有一个没完成,所有人都得等着。
当线程到达关卡点,调用await,await会被阻塞,直到所有线程都到达关卡点。如果所偶线程都到达了关卡点,关卡被成功突破,这样所有的被阻塞的线程会被释放,此时关卡被重置,以备下一次使用。如果调用await超时,或者阻塞中的线程被中断,那么关卡就被置为失败。所有的对await未完成的调用,都会通过抛出BrokenBarrierException而终止。
10个人去春游,规定达到一个地点后才能继续前行:
/**    
* @Title: CyclicBarrierWorker.java  
* @Package synchronizer.barrier  
* @Description: (用一句话描述该文件做什么)  
* @Author 落叶飞翔的蜗牛
* @Date 2017年12月16日 下午6:19:50  
* @Version V1.0    
*/
package synchronizer.barrier;


import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;


/**  
* @ClassName: CyclicBarrierWorker  
* @Description: (这里用一句话描述这个类的作用)  
* @Author 落叶飞翔的蜗牛 
* @Date 2017年12月16日 下午6:19:50    
*/
public class CyclicBarrierWorker implements Runnable {
	private int id;
    private CyclicBarrier barrier;


    public CyclicBarrierWorker(int id, final CyclicBarrier barrier) {
        this.id = id;
        this.barrier = barrier;
    }


    @Override
    public void run() {
        // TODO Auto-generated method stub
        try {
            System.out.println("第" + id + "个人到达,并等待");
            barrier.await(); // 大家等待最后一个线程到达
        } catch (InterruptedException | BrokenBarrierException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

/**    
* @Title: CyclicBarrierWorkerTest.java  
* @Package synchronizer.barrier  
* @Description: (测试类)  
* @Author 落叶飞翔的蜗牛
* @Date 2017年12月16日 下午6:27:04  
* @Version V1.0    
*/
package synchronizer.barrier;


import java.util.concurrent.CyclicBarrier;


import org.junit.Test;


/**  
* @ClassName: CyclicBarrierWorkerTest  
* @Description: (测试类)  
* @Author 落叶飞翔的蜗牛 
* @Date 2017年12月16日 下午6:27:04    
*/
public class CyclicBarrierWorkerTest {
	
	@Test
	public void test() {
		int num = 10;
	    CyclicBarrier barrier = new CyclicBarrier(num, new Runnable() {
	        @Override
	        public void run() {
	        	//关卡通过后执行以下
	            System.out.println("一起走啊!");
	        }
	    });
	    
	    for (int i = 1; i <= num; i++) {
	        new Thread(new CyclicBarrierWorker(i, barrier)).start();
	    }
	}
}

猜你喜欢

转载自blog.csdn.net/shixuetanlang/article/details/78828628
今日推荐