几种同步工具介绍

下面的内容基本上来自于《Java并发编程实践》, 留个记录~

 

一,CountDownLatch

CountDownLatch是一个灵活的闭锁的实现,允许一个或多个线程等待一个事件集的发生。

闭锁的状态包括一个计数器,初始化为一个正数,用来表现需要等待的事件数。countDown方法对计数器做减操作,表示一个事件已经发生了,而await方法会一直阻塞直到计数器为0,或者等待线程中断以及超时。

 

下面是n个线程并发执行的例子

public class CountDownLatchTest {
	public long concurrentTasks(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();
						task.run();
					} catch (InterruptedException e) {
						e.printStackTrace();
					} finally {
						endGate.countDown();
					}
				}
			};
			t.start();
		}
		
		long start= System.nanoTime();
		startGate.countDown();
		endGate.await();
		long end = System.nanoTime();
		return end - start;
	}
}

 上面的例子使用了两个阀门,一个“开始阀门”, 一个“结束阀门”。开始阀门将计数器初始化为1,结束阀门将计数器初始化为线程的数量。每一个工作线程做的第一件事是等待开始阀门的打开,这样能确保所有的线程都准备好再开始工作。每个线程的最后一件事是将结束阀门减1,这样是控制线程有效的等待,直到最后一个线程完成任务,这样就能计算线程整个的用时了。

 

二, FutureTask

FutureTask的计算通过Callable实现的,它等价于一个可以携带结果的Runnable,并且有3个状态: 等待,运行和完成。完成包括所有计算以任意的方式结束,包括正常、取消和异常。一旦FutureTask进入完成状态,将会永远停止在这个状态上。

Future.get的行为依赖于任务的状态。如果它已经完成,get可以立即得到结果,否则会阻塞直到任务转入完成状态,然后返回结果或抛出异常。FutureTask把计算的结果从运行计算的线程传送到需要这个结果的线程: FutureTask的规约保证了这种传递建立在结果的安全发布基础之上。

public class FutureTaskTest {
	private FutureTask<String> task = new FutureTask<String>(new Callable<String>() {
		@Override
		public String call() throws Exception {
			System.out.println("Thread name1: " + Thread.currentThread().getName());
			Thread.sleep(3000);
			return "AAA";
		}
	});
	
	public String invokeTask() throws InterruptedException, ExecutionException {
		String pattern = "HH:mm:ss";
		DateFormat format = new SimpleDateFormat(pattern);
		System.out.println("begin: " + format.format(new Date()));
		new Thread(task).start();
		System.out.println("Thread name2: " + Thread.currentThread().getName());
		String result = task.get();
		System.out.println("end: " + format.format(new Date()));
		System.out.println("result: " + result);
		return result;
	}
	
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		FutureTaskTest test = new FutureTaskTest();
		test.invokeTask();
	}
}

 运算的结果如下:

begin: 11:17:46
Thread name2: main
Thread name1: Thread-0
end: 11:17:49
result: AAA
由此可知,在执行get方法时,main线程等待FutureTask的计算结果,处于阻塞状态。

 

 

三, 信号量

信号量用来控制能够同时访问某特定资源的活动的数量,或者同时执行某一给定操作的数量。计数信号量可以用来实现资源池或者给一个容器限定边界。

信号量可以用来实现资源池,比如数据库连接池。有一个定长的池,当它为空时,你向它请求资源会失败。构建这种池很容易,然而当池为空时,你真正需要做的是阻塞它,然后在不为空时,再次解除阻塞。如果你以池的大小初始化一个Semaphore,在你从池中获取资源之前,你应该用acquire方法获取一个许可,调用release把许可放回资源池。acquire会一直阻塞,直到池不为空。

public class SemaphoreTest {
	private Semaphore semaphore;
	private List<String> stringList = new ArrayList<String>();

	public SemaphoreTest(int n) {
		semaphore = new Semaphore(n);
	}
	
	public void addElement(String ele) {
		try {
			semaphore.acquire();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		stringList.add(ele);
	}
	
	public String removeElement() {
		String ele = stringList.remove(stringList.size() - 1);
		semaphore.release();
		return ele;
	}
}

 

 

 

你可能感兴趣的:(Semaphore,CountDownLatch)