Java并发编程与技术内幕:Callable、Future、FutureTask、CompletionService

  在上一文章中,笔者介绍了线程池及其内部的原理。今天主要讲的也是和线程相关的内容。一般情况下,使用Runnable接口、Thread实现的线程我们都是无法返回结果的。但是如果对一些场合需要线程返回的结果。就要使用用Callable、Future、FutureTask、CompletionService这几个类。Callable只能在ExecutorService的线程池中跑,但有返回结果,也可以通过返回的Future对象查询执行状态。Future 本身也是一种设计模式,它是用来取得异步任务的结果.

如果在主线程中需要执行比较耗时的操作时,但又不想阻塞主线程时,可以把这些作业交给Future对象在后台完成,当主线程将来需要时,就可以通过Future对象获得后台作业的计算结果或者执行状态。

public class Aa {
	public static void main(String[] args) throws Exception {
		System.out.println("main----开始");
		ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
//		submit里是一个Callable<T> task 接口,这里可以不写实现类,使用匿名内部类代替。在匿名内部类接口的方法体内覆盖父类即可。
		Future<String> future = cachedThreadPool.submit(new Callable<String>() {

			@Override
			public String call() throws Exception {
				// TODO Auto-generated method stub
				Thread.sleep(5000);
				return "hehehehe";
			}
		});
		System.out.println("future----开始");
		System.out.println(future.get());
		System.out.println("main----结束");
	}
}

注意:假如有多个future,谁先get谁就先返回。例如以下例子future先get,则future先返回;future3最后get则future3最后返回。

public class Aa {
	public static void main(String[] args) throws Exception {
		System.out.println("main----开始");
		ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
//		submit里是一个Callable<T> task 接口,这里可以不写实现类,使用匿名内部类代替。在匿名内部类接口的方法体内覆盖父类即可。
		Future<String> future = cachedThreadPool.submit(new Callable<String>() {

			@Override
			public String call() throws Exception {
				// TODO Auto-generated method stub
				Thread.sleep(1000);
				return "hehehehe";
			}
		});
		Future<String> future2 = cachedThreadPool.submit(new Callable<String>() {
			
			@Override
			public String call() throws Exception {
				// TODO Auto-generated method stub
				Thread.sleep(15000);
				return "hehehehe2";
			}
		});
		Future<String> future3 = cachedThreadPool.submit(new Callable<String>() {
			
			@Override
			public String call() throws Exception {
				// TODO Auto-generated method stub
				Thread.sleep(1000);
				return "hehehehe3";
			}
		});
	 
		System.out.println(future.get());
		System.out.println(future2.get());
		System.out.println(future3.get());

		
	}
}


CompletionService
这个光看其单词,就可以猜到它应该是一个线程执行完成后相关的服务,没错。它就是一个将线程池执行结果放入到一个Blockqueueing的类。那么它和Future或FutureTask有什么不同呢?其实在上面的例子中,笔者用的实例可能不太好。如果在线程池中我们使用Future或FutureTask来取得返回结果,比如。我们开了100条线程。但是这些线程的执行时间是未知的。但是我们又需要返回结果。每执行一条线程就根据结果做一次相应的操作。如果是Future或FutureTask。我们只能通过一个循环,不断的遍历线程池里的线程。取得其执行状态。然后再来取结果。这样效率就太低了,有可能发生一条线程执行完毕了,但我们不能立刻知道它处理完成了。还得通过一个循环来判断。基本上面的这种问题,所以出了CompletionService。
     CompletionService原理不是很难,它就是将一组线程的执行结果放入一个BlockQueueing当中。这里线程的执行结果放入到Blockqueue的顺序只和这个线程的执行时间有关。和它们的启动顺序无关。并且你无需自己在去写很多判断哪个线程是否执行完成,它里面会去帮你处理。

CompletionService和上面介绍的future不同:future是谁先get谁先返回,completionservice是谁先完成任务,谁就先返回。

例如以下例子

因为"hehehe2"所在线程休眠两秒,所"hehehe2”所在线程最后返回。

可以看出,结果的输出和线程的放入顺序无关系。每一个线程执行成功后,立刻就输出。

public class Vv {
	public static void main(String[] args) throws Exception {
		ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
		
		 
		CompletionService<Object> completionService = new ExecutorCompletionService<>(cachedThreadPool);
			completionService.submit(new Callable<Object>() {
			@Override
			public Object call() throws Exception {
				return "hehehe";
			}
		});
			completionService.submit(new Callable<Object>() {
				@Override
				public Object call() throws Exception {
//					睡眠2秒钟  -----------------------------
					Thread.sleep(2000);
					return "hehehe2";
				}
			});
			completionService.submit(new Callable<Object>() {
				@Override
				public Object call() throws Exception {
					return "hehehe3";
				}
			});
			int i=1;
			while (i<50) {
				System.out.println(completionService.take().get());
				
			}
		
		
	}
}

以上内容的参考链接:http://blog.csdn.net/evankaka/article/details/51610635

我自己的小结

---http://blog.csdn.net/evankaka/article/details/51489322

除了使用execute,也可以使用submit,它们两个的区别是一个使用有返回值(submit),一个没有返回值。submit的方法很适应于生产者-消费者模式,通过和Future结合一起使用,可以起到如果线程没有返回结果,就阻塞当前线程等待线程 池结果返回。
注意,submit中的线程要实现接口Callable
ThreadPoolExecutor技术内幕:经过上面的过程,基本上可以掌握线程池的一些基本用法。下面再来看看JAVA中线程池的源码实现。

----http://blog.csdn.net/evankaka/article/details/5161063
Java并发编程与技术内幕:Callable、Future、FutureTask、CompletionService
一般情况下,使用Runnable接口、Thread实现的线程我们都是无法返回结果的。但是如果对一些场合需要线程返回的结果。就要使用用Callable、Future、FutureTask、CompletionService这几个类。
我的理解:几种返回值处理用于不同的情形,注意辨别。
Future、FutureTask、CompletionService异同:
FutureTask和Future 的一个实现,他两可以通过Excutor(线程池) 来执行,也可传递给Thread(非线程池)对象执行。而CompletionService需要和线程池结合使用。
如果在主线程中需要执行比较耗时的操作时,但又不想阻塞主线程时,可以把这些作业交给Future对象在后台完成,当主线程将来需要时,就可以通过Future对象获得后台作业的计算结果或者执行状态。
Executor框架利用FutureTask来完成异步任务,并可以用来进行任何潜在的耗时的计算。一般FutureTask多用于耗时的计算,主线程可以在完成自己的任务后,再去获取结果。
CompletionService它就是一个将线程池执行结果放入到一个Blockqueueing的类。
那么它和Future或FutureTask有什么不同呢?其实在上面的例子中,笔者用的实例可能不太好。如果在线程池中我们使用Future或FutureTask来取得返回结果,比如。我们开了100条线程。但是这些线程的执行时间是未知的。但是我们又需要返回结果。每执行一条线程就根据结果做一次相应的操作。如果是Future或FutureTask。我们只能通过一个循环,不断的遍历线程池里的线程。取得其执行状态。然后再来取结果。这样效率就太低了,有可能发生一条线程执行完毕了,但我们不能立刻知道它处理完成了。还得通过一个循环来判断。

Future:从输出结果可以看出,我们只能一个一个阻塞的取出。这中间肯定会浪费一定的时间在等待上。如7返回了。但是前面1-6都没有返回。那么7就得等1-6输出才能输出。

CompletionService:结果的输出和线程的放入顺序无关系。哪一个线程执行成功后,立刻就输出。

2 有返回值的需要构造函数一定要注入一个Callable对象

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值