声明:文章内容全都是自己的学习总结,如有不对的地方请大家帮忙指出。有需要沟通交流的可加我QQ群:425120333
前面提到的实现Runnable接口和继承Thread类的方式都是没有返回值的,而这次要是的通过实现Callable接口的方式实现是能够有返回值的,
集体的返回值类型可根据需要自己指定,这个返回值可以是一个对象,也可以是一个容器类(根据需求),既然有返回值,那就必须要获取到,
而Futura就是为了获取Callable在多线程情况下的返回值。
看一个简单的例子:
public class TestCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
return 1;
}
public static void main(String[] args) {
FutureTask<Integer> futureTask = new FutureTask<Integer>(new TestCallable());
new Thread(futureTask).start();
try {
//get()方法是一个阻塞方法,如果线程未执行好,就会一直阻塞
Integer result = futureTask.get();
System.out.println(result);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
这里通过FutureTask来获取TestCallable执行结果的返回值, FutureTask实现了两个接口,Runnable和Future,所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值。
关于Future模式这里有个介绍可以参考下 http://openhome.cc/Gossip/DesignPattern/FuturePattern.htm
接下来通过线程池来获取Callable的结果,相比较前一中,会简单一些
public class TestCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
return 1;
}
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService service = Executors.newCachedThreadPool();
Future<Integer> future = service.submit(new TestCallable());
try {
//get()方法是一个阻塞方法,如果线程未执行好,就会一直阻塞
Integer result = future.get();
System.out.println(result);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
看到这里,有人可能会提出疑惑,我的线程池如果是提交多个任务的,那怎么保证每个的结果能对应下呢?
这里有两种解决方案,第一种是按照提交到线程池的顺序挨个获取返回值,代码示例:
public class TestCallable implements Callable<Integer> {
private Integer id;
public TestCallable(int id) {
this.id = id;
}
@Override
public Integer call() throws Exception {
return id;
}
public static void main(String[] args) throws InterruptedException, ExecutionException {
//构建一个存放Future对象的容器,循环遍历出其中的每一个
List<Future<Integer>> futureList = new ArrayList<Future<Integer>>();
ExecutorService service = Executors.newCachedThreadPool();
for (int i = 0; i < 5; i++) {
Future<Integer> future = service.submit(new TestCallable(i));
futureList.add(future);
}
for (Future<Integer> future : futureList) {
System.out.println(future.get());
}
}
}
控制台显示:
0
1
2
3
4
这样虽然能够按照顺序获取到相应的结果,但如果其中某个阻塞住的话,后面的就无法获取结果值了(除非知道是那个阻塞住,从而跳过);
第二种方法是通过使用CompletionService,示例如下
public class TestCallable implements Callable<Integer> {
private Integer id;
public TestCallable(int id) {
this.id = id;
}
@Override
public Integer call() throws Exception {
if (id == 3) {
//加个睡眠时间使得结果更加明显
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return id;
}
public static void main(String[] args) throws Exception {
ExecutorService service = Executors.newCachedThreadPool();
CompletionService<Integer> completionService = new ExecutorCompletionService<Integer>(
service);
for (int i = 0; i < 5; i++) {
completionService.submit(new TestCallable(i));
}
for (int i = 0; i < 5; i++) {
System.out.println(completionService.take().get());
}
}
}
控制台显示:
0
2
1
4
3
提交到CompletionService中的Future是按照完成的顺序排列的,和提交的先后没有影响,因为id=3时加了一个睡眠,所以3总是最后一个输出,前面几个输出顺序不一定。