在上一文章中,笔者介绍了线程池及其内部的原理。今天主要讲的也是和线程相关的内容。一般情况下,使用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());
}
}
这个光看其单词,就可以猜到它应该是一个线程执行完成后相关的服务,没错。它就是一个将线程池执行结果放入到一个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对象