博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Runnable和Callable的区别
阅读量:2454 次
发布时间:2019-05-10

本文共 5523 字,大约阅读时间需要 18 分钟。

Runnable和Callable的区别

Callable接口

public interface Callable
{
V call() throws Exception;}

Runnable接口

public interface Runnable {
public abstract void run();}

Runnable和Callable的区别

相同点

1、两者都是接口;(废话)

2、两者都可用来编写多线程程序;
3、两者都需要调用Thread.start()启动线程;

不同点

1、两者最大的不同点是:实现Callable接口的任务线程能返回执行结果;而实现Runnable接口的任务线程不能返回结果;

2、Callable接口的call()方法允许抛出异常;而Runnable接口的run()方法的异常只能在内部消化,不能继续上抛;

注意点

Callable接口支持返回执行结果,此时需要调用FutureTask.get()方法实现,此方法会阻塞主线程直到获取‘将来’结果;当不调用此方法时,主线程不会阻塞!

Callable工作的Demo

package com.callable.runnable;import java.util.concurrent.Callable;import java.util.concurrent.ExecutionException;import java.util.concurrent.FutureTask;/** * Created on 2016/5/18. */public class CallableImpl implements Callable
{
public CallableImpl(String acceptStr) {
this.acceptStr = acceptStr; } private String acceptStr; @Override public String call() throws Exception {
// 任务阻塞 1 秒 Thread.sleep(1000); return this.acceptStr + " append some chars and return it!"; } public static void main(String[] args) throws ExecutionException, InterruptedException {
Callable
callable = new CallableImpl("my callable test!"); FutureTask
task = new FutureTask<>(callable); long beginTime = System.currentTimeMillis(); // 创建线程 new Thread(task).start(); // 调用get()阻塞主线程,反之,线程不会阻塞 String result = task.get(); long endTime = System.currentTimeMillis(); System.out.println("hello : " + result); System.out.println("cast : " + (endTime - beginTime) / 1000 + " second!"); }}

ExcutorService中的excutor和submit方法的区别

两者都是将一个线程任务添加到线程池中并执行;

1、excutor没有返回值,submit有返回值,并且返回执行结果Future对象
2、excutor不能提交Callable任务,只能提交Runnable任务,submit两者任务都可以提交
3、在submit中提交Runnable任务,会返回执行结果Future对象,但是Future调用get方法将返回null(Runnable没有返回值)

使用场景:

使用多线程任务校验被保人数据;

1、每个被保人都是一个线程任务,
2、每个线程任务执行完都需要告诉主线程执行成功还是失败
3、这里需要submit提交Callable任务返回Future对象,并通过Future.get方法来获取执行结果

FutureTask

FutureTask实现了RunnableFuture,RunnableFuture既实现了Runnbale又实现了Futrue这两个接口;它两者的结合体

FutureTask又包装了Callable(如果是Runnable最终会转化为Callable);
FutureTask可以通过Thread包装来直接执行,也可以提交给ExcutorService来执行,并可以直接通过get方法来获取执行结果;

ExecutorCompletionService

使用ExecutorCompletionService提交任务后会将执行结果放到阻塞队列中,使用take方法会得到结果,哪个任务先执行完成就先获取到这个任务的执行结果;

原理:在ExecutorCompletionService维护了一个QueueingFuture(队列任务),当通过ExecutorCompletionService提交的任务执行完成后,将结果放入QueueingFuture中;然后通过take和poll方法获取执行结果时会阻塞线程,直到当QueueingFuture中有结果时就会立即返回;
如果自己维护一个list来存放future执行结果,会导致的问题是:这样通过future.get方法来获取执行结果只能一个一个阻塞取出执行结果,如果后面的任务可能会先执行完成,后面的任务只有等待前面的任务执行完成得到结果后才能获取到结果,这样就浪费一定的时间在等待执行任务的结果上了。可以用ExecutorCompletionService来解决这个问题的。
总结:
1、自己创建一个集合来保存Future存根并循环调用其返回结果的时候,主线程并不能保证首先获得的是最先完成任务的线程返回值。它只是按加入线程池的顺序返回。因为take方法是阻塞方法,后面的任务完成了,前面的任务却没有完成,主程序就那样等待在那儿,只到前面的完成了,它才知道原来后面的也完成了。
2、使用CompletionService来维护处理线程的返回结果时,主线程总是能够拿到最先完成的任务的返回值,而不管它们加入线程池的顺序。
3、CompletionService的实现是维护了一个保存Future的BlockingQueque。只有当这个Future的任务状态是结束的时候,才会加入到这个Queque中,take()方法其实就是Producer-Consumer中的Consumer。它会从Queue中取出Future对象,如果Queue是空的,就会阻塞在那里,直到有完成的Future对象加入到Queue中。也就是先完成的必定先被取出,这样就减少了不必要的等待时间。
注意:使用Future获取多个任务的执行结果时,如果其中一个任务出现异常,就会直接中断不会获取后面任务的执行结果了,但是不会中断其他任务,其他任务会正常运行,只是结果无法获取了;如果需要中断其他任务就需要在捕获到异常后执行ExecutorService.shutdownNow方法来关闭线程,也可以通过Future.cancle方法来中断
ExecutorService中shutdownNow和shutdown的区别:
shutdownNow表示立刻关闭线程池并中断正在执行的任务;
shutdown表示线程池中的任务全部执行完成后再关闭线程池

示例代码

使用CompletionService维护结果

System.out.println("start...");        long begin = System.currentTimeMillis();        // 创建一个线程池        int taskSize = 5;        ExecutorService pool = Executors.newFixedThreadPool(taskSize);        ExecutorCompletionService
completionService = new ExecutorCompletionService
(pool); try {
for (int i = 1; i <= taskSize; i++) {
MyCallable callable = new MyCallable(new Integer(i)); completionService.submit(callable); } pool.shutdown(); for (int i = 1; i <= taskSize; i++) {
System.out.println(completionService.take().get()); } } catch (Exception e) {
e.printStackTrace(); } // 关闭线程池 long end = System.currentTimeMillis(); System.out.println("end..excute time:" + (end - begin) + "ms"); return;

自己创建list维护执行结果

System.out.println("start...");                long begin = System.currentTimeMillis();                ExecutorService pool = null;                try {
int taskSize = 5; // 创建一个线程池 pool = Executors.newFixedThreadPool(taskSize); // 创建多个返回值的任务 List
list = Lists.newArrayList(); for (int i = 1; i <= taskSize; i++) {
MyCallable callable = new MyCallable(new Integer(i)); Future
future = pool.submit(callable); list.add(future); System.out.println("已添加" + i); } for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i).get().toString()); } } catch (Exception e) {
e.printStackTrace(); } // 关闭线程池 pool.shutdown(); long end = System.currentTimeMillis(); System.out.println("end..excute time:" + (end - begin) / 1000 + "s"); return;

转载地址:http://idchb.baihongyu.com/

你可能感兴趣的文章
斯德哥尔摩皇家花园_斯德哥尔摩美食节的美味翻译
查看>>
你鼓舞了我是世界杯主题曲吗_10篇关于开放式领导原则的鼓舞人心的书
查看>>
raspberry pi_Raspberry Pi答案:我的仓鼠是懒惰的还是超级运动员?
查看>>
openstack安装指南_使用OpenStack的6大新指南
查看>>
菜鸟侦探挑战数据分析pdf_历史学家和侦探使用开源工具跟踪数据
查看>>
Linux问题的故障排除过程
查看>>
开源 协作工具_使用开源工具分析,协作和共享研究
查看>>
开源轻量级bi_Twisource:一种轻量级的开源社交媒体解决方案
查看>>
outlook 替代_前5名:Outlook,My Linux Story等的替代品
查看>>
前5名:Red Hat CEO Linux故事,LibreOffice 5年,Twitter开源主管等
查看>>
开源语法解释器_抓住机会解释开源
查看>>
sonic pi_前5名:Linux,Sonic Pi,LibreOffice等
查看>>
owncloud_本周热门文章5:ownCloud创始人专访,Raspberry Pi上的Docker等
查看>>
slack 替代_前5名:Linux安全性,Slack聊天替代品,Vagrant等
查看>>
cobol 变量(1:1)_每周排名前5的文章:COBOL,AsciiDoc等
查看>>
客户开源的重要性_在开源世界中面对面的重要性
查看>>
业务模型管理平台 开源_开源模型适用于电子学习业务
查看>>
raspberry pi_将Raspberry Pi变成便携式流式相机
查看>>
dancer.js_与轻量级的Perl Web应用程序框架Dancer一起旋转
查看>>
公众号精选评论点赞_9月评论:前10名和编辑精选
查看>>