并发编程系列之Callable和Runnable的不同?

Posted smileNicky

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了并发编程系列之Callable和Runnable的不同?相关的知识,希望对你有一定的参考价值。

本博客学习要点:

  • 1、了解Runnable的原理和不足
  • 2、掌握怎么使用Callable实现任务
  • 3、对比Runnable和Callable的不同

ps:基于Jdk1.8看源码

1、Runnable入门实例

并发编程系列之Callable和Runnable的不同?在学习并发多线程的过程中,很多读者都知道怎么实现Runnable,下面是一道经典的例子

public  static void main(String[] args) {
	Thread t  = new Thread(new RunnableTask ());
	t.start();
}
static class RunnableTask implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}

2、Runnable有什么缺陷?

对于Callable,可能有些读者就不是很熟悉了,在上一章节多线程基础知识的学习中,我们知道了Runnable和Callable其实可以用来表示多线程的任务,而在多线程的方法中,我们是没有找到可以传入Callable对象的具体方法的


然后Callable要怎么使用?为什么要设计出Callable?Runnable是不是有什么缺陷?ok,还是先看看Runnable的源码:可以看出Runable其实就是一个接口,同时使用了java的函数式函数@FunctionalInterface,所以是可以支持lambda表达式的,这是jdk8中的新特性

@FunctionalInterface
public interface Runnable {
    /**
     * When an object implementing interface <code>Runnable</code> is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see     java.lang.Thread#run()
     */
    public abstract void run();
}

然后Runnable有什么缺陷?

  • (1)、没有返回值
    Runnablerun方法是void类型的,没有返回值,虽然可以在实现的run方法里写入日志文件或者修改某个共享的对象的办法,来达到保存线程执行结果的目的,不过这样实现确实比较麻烦。
    实际上,在很多情况下执行一个子线程时,我们都希望能得到执行的任务的结果,可是 Runnable 不能返回一个返回值,这是它第一个非常严重的缺陷。

  • (2)不能抛出 checked Exception
    Runnable是不能抛出 checked Exception的,run方法是不允许在声明throws Exception的, 且run方法内无法 throw 出 checked Exception,除非使用try catch进行处理

Runnable runnable = new Runnable() {
    /**
     * run方法是不允许在声明throws Exception的,
     * 且run方法内无法 throw 出 checked Exception,
     * 除非使用try catch进行处理
     */
    @Override
    public void run() {
        try {
            throw new IOException();
        }catch (IOException e) {
            e.printStackTrace();
        }
    }
};
}

3、Runnable 为什么设计成这样?

知道了Runnable的缺陷后,我们可能会好奇为什么之前这样设计?假设run方法可以抛出checked Exception,也可以有返回值,也无济于事,因为run方法调用,我们是通过Thread类或者是线程池IUC的api去调用的,所以我们是不可以在外层就捕获到Exception的。
就算它能有一个返回值,我们也很难把这个返回值利用到,如果真的想弥补 Runnable 的这两个缺陷,可以用下面的补救措施,也就是使用 Callable

4、Callable需要怎么调用?

调用Callable任务有两种使用方法,一种是使用线程池,另外一种是使用FutureTask调用

使用线程池的方法:

public static void callableExecutorService() throws InterruptedException, ExecutionException {
     ExecutorService service = new ThreadPoolExecutor(10, 10,
             60L, TimeUnit.SECONDS,
             new ArrayBlockingQueue(10));

     Future<Integer> future = service.submit(new CallableTask());
     Thread.sleep(3000);
     System.out.println("future is done?" + future.isDone());
     if (future.isDone()) {
         System.out.println("callableTask返回参数:"+future.get());
     }
     service.shutdown();
}

static class CallableTask implements Callable<Integer> {
    @Override
    public Integer call() {
        return ThreadLocalRandom.current().ints(0, (99 + 1)).limit(1).findFirst().getAsInt();
    }
}

使用FutureTask的方法:

public static void callableFutureTask() throws InterruptedException, ExecutionException {
    CallableTask task = new CallableTask();
    FutureTask futureTask = new FutureTask(task);
    Thread t = new Thread(futureTask);
    t.start();
    Thread.sleep(1000L);
    System.out.println("task result:" + futureTask.get());
}
static class CallableTask implements Callable<Integer> {
    @Override
    public Integer call() {
        return ThreadLocalRandom.current().ints(0, (99 + 1)).limit(1).findFirst().getAsInt();
    }
}

5、Callable 和 Runnable 的不同之处

对于Runnable前面已经介绍过,使用现在翻下Callable的源码:可以看出Callable本质也是一个接口,也和Runnable一样也支持函数式接口,不过不同的是Callable使用了V这个泛型,所以是可以支持返回值的,而且也有throws Exception,所以可以获取到checked Exception

@FunctionalInterface
public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}

ok,可以归纳一下Callable和Runnable的不同:

1、方法名,Runnable的方法名是run,而Callable的方法名是call
2、抛出异常,Runnable不支持抛出异常,而Callable支持抛出checked Exception
3、返回值,Runnable不支持返回值,而Callable是支持返回值的
4、是否支持Future使用,Runnable不支持,Callable是可以组合线程池或者FutureTask一起使用,同时可以将结果返回给Future,通过 Future 可以了解任务执行情况,或者取消任务的执行

以上是关于并发编程系列之Callable和Runnable的不同?的主要内容,如果未能解决你的问题,请参考以下文章

Java并发多线程编程——Callable和Runnable接口的区别

并发编程之Callable异步,Future模式

Java并发编程系列之十五:Executor框架

Callable, Runnable, Future, FutureTask

java并发编程--Runnable Callable及Future

Java并发编程之线程创建和启动(ThreadRunnableCallable和Future)