Java多线程编程--(10)学习Java5.0 并发编程包--线程工具类

上篇介绍了Semaphore 和 CyclicBarrier,这次说一下另外两个:CountDownLatch 和 Exchanger:

CountDownLatch

倒计数器!用这个类,可以很好的模拟一个运动会场景:3个运动员1个裁判,运动员准备好后,裁判发令,然后运动员开跑,运动员结束后,告知裁判,等所有运动员都返回终点,裁判宣布比赛结果:

package cn.test;

import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class CountDownLatchTest {

	public static void main(String[] args) throws InterruptedException, ExecutionException {
		
		// 裁判的计数器,等待所有运动员准备好后,发令开跑
		final CountDownLatch refereeCountDownLatch = new CountDownLatch(1);
		// 运动员的计数器,一个3个运动员,每个运动员跑完后通过计数器报到,所有运动员都跑完后,裁判宣布成绩
		final CountDownLatch athletesCountDownLatch = new CountDownLatch(3);
		// 创建3个线程的线程池,作为3个待跑的运动员
		ExecutorService athletesThreadPool = Executors.newFixedThreadPool(3);
		// 因为我们最后去宣布成绩,所以我们希望运动员线程执行完毕后,返回一个结果,所以我们此处用CompletionService
		CompletionService<String> completionService = new ExecutorCompletionService<String>(athletesThreadPool);
		// 提交3个任务,作为3个运动员的跑步过程
		for(int i=0;i<3;i++){
			
			completionService.submit(new Callable<String>(){

				@Override
				public String call() throws Exception {
					
					System.out.println(Thread.currentThread().getName() + " 进入准备起跑阶段!");
					// 通知裁判
					refereeCountDownLatch.await();
					// 开始起跑,随即睡一段时间,来模拟跑步过程 new Random().nextDouble()返回0-1之间的数字
					long useTime = (long)(new Random().nextDouble() * 10000);
					Thread.sleep(useTime);
					System.out.println(Thread.currentThread().getName() + " 跑步结束!耗时:" + useTime);
					// 报告计数器,并且返回结果!
					athletesCountDownLatch.countDown();
					return "运动员:" + Thread.currentThread().getName();
				}});
		}
		// 主线程模拟裁判,先等上2秒钟,让运动员准备就绪(这里可以改进为当运动员准备好后,通知裁判!)
		Thread.sleep(2000);
		// 裁判宣布比赛可以
		System.out.println("裁判宣布比赛开始:");
		refereeCountDownLatch.countDown();
		// 裁判阻塞,等待结果
		athletesCountDownLatch.await();
		// 当所有运动员结束后,裁判宣布结果。CompletionService会按照执行完毕的顺序,依次返回线程执行结果
		Future<String> firstAthlete = completionService.take();
		Future<String> secondAthlete = completionService.take();
		Future<String> thridAthlete = completionService.take();
		
		System.out.println("本次运动会,冠军:" + firstAthlete.get());
		System.out.println("本次运动会,亚军:" + secondAthlete.get());
		System.out.println("本次运动会,季军:" + thridAthlete.get());
		
				
	}

}

比赛结果为:

pool-1-thread-1 进入准备起跑阶段!
pool-1-thread-3 进入准备起跑阶段!
pool-1-thread-2 进入准备起跑阶段!
裁判宣布比赛开始:
pool-1-thread-3 跑步结束!耗时:8577
pool-1-thread-1 跑步结束!耗时:8686
pool-1-thread-2 跑步结束!耗时:9762
本次运动会,冠军:运动员:pool-1-thread-3
本次运动会,亚军:运动员:pool-1-thread-1
本次运动会,季军:运动员:pool-1-thread-2

创建CountDownLatch会接受一个整形参数,表示计数器初始值,调用对象的countDown()方法,会将这个初始值减1,直到0为止。线程调用对象的await()方法,如果计数器的值不为0,则阻塞,否则,该调用直接返回!我们上述过程,先让3个运动员线程全部阻塞在初始值为1的计数器上,裁判调用一下计数器的countDown()方法,3个运动员开始跑步。然后裁判await在一个初始值为3的计数器上,每个运动员结束后,调用一下这个计数器的countDown()方法,当所有运动员结束,裁判从计数器的await方法返回,宣布结果!这里我们还结合使用了ExecutorService 和 CompletionService,具体用法,可参考前面介绍!

【Exchanger】

线程变量交换器!这个可以实现两个线程在运行间,随时交换两个值!我们考虑一个例子,卖家和买家。卖家线程拿着货说我要卖,然后等待,买家线程来了,出钱,换得东西,两个线程继续进行:

package cn.test;

import java.util.concurrent.Exchanger;

public class ExchangerTest {

	public static void main(String[] args) {
		
		final Exchanger<String> exchanger = new Exchanger<String>();
		// 先创建一个卖家线程,将东西准备好,开始卖
		new Thread(new Runnable(){

			@Override
			public void run() {
				
				System.out.println(Thread.currentThread().getName() + " : 卖东西了.....一手交钱一手交货!");
				try {
					String goods = "Good TV!";
					String getBack = exchanger.exchange(goods);
					System.out.println(Thread.currentThread().getName() + " : " + goods + " 卖出去了,得到了 " + getBack);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				
			}}, "Seller").start();
		
		// 再创建一个买家线程,将钱准备好,开始买
		new Thread(new Runnable(){

			@Override
			public void run() {
				
				System.out.println(Thread.currentThread().getName() + " : 买东西了.....一手给货一手付钱!");
				try {
					String money = "2000$";
					String getBack = exchanger.exchange(money);
					System.out.println(Thread.currentThread().getName()  + " : " + money + " 花了,得到了 " + getBack);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				
			}}, "Buyer").start();
	}

}


交易结果为:

Seller : 卖东西了.....一手交钱一手交货!
Buyer : 买东西了.....一手给货一手付钱!
Buyer : 2000$ 花了,得到了 Good TV!
Seller : Good TV! 卖出去了,得到了 2000$

我们看到两个线程互相交互了各自的一个局部变量!创建Exchanger对象,第一个线程调用对象的exchange方法(传递一个参数,即用于交换的变量),阻塞,等待另一个线程在这个对象上同样调用这个exhange方法。第二个线程调用后,两者互换调用参数,该方法返回!Exchanger是一个支持泛型的类,泛型就是用于交换的值的类型!

 

以上就是并发包中提到的4个好玩的线程控制工具类!大家了解一下即可,如果在实际中有这方面的需求,可以考虑能不能用这几个好玩的工具类来简化自己的线程控制代码!

你可能感兴趣的:(java,多线程,编程,String,Semaphore,Random)