你有一个思想,我有一个思想,我们交换后,一个人就有两个思想
If you can NOT explain it simply, you do NOT understand it well enough
现陆续将Demo代码和技术文章整理在一起 Github实践精选 ,方便大家阅读查看,本文同样收录在此,觉得不错,还请Star
前言创建线程有几种方式?这个问题的答案应该是可以脱口而出的吧
继承 Thread 类
实现 Runnable 接口
但这两种方式创建的线程是属于”三无产品“:
没有参数
没有返回值
没办法抛出异常
class MyThread implements Runnable{ @Override public void run() { log.info("my thread"); } }Runnable 接口是 JDK1.0 的核心产物
/** * @since JDK1.0 */ @FunctionalInterface public interface Runnable { public abstract void run(); }用着 “三无产品” 总是有一些弊端,其中没办法拿到返回值是最让人不能忍的,于是 Callable 就诞生了
Callable又是 Doug Lea 大师,又是 Java 1.5 这个神奇的版本
/** * @see Executor * @since 1.5 * @author Doug Lea * @param <V> the result type of method {@code call} */ @FunctionalInterface public interface Callable<V> { V call() throws Exception; }Callable 是一个泛型接口,里面只有一个 call() 方法,该方法可以返回泛型值 V ,使用起来就像这样:
Callable<String> callable = () -> { // Perform some computation Thread.sleep(2000); return "Return some result"; };二者都是函数式接口,里面都仅有一个方法,使用上又是如此相似,除了有无返回值,Runnable 与 Callable 就点差别吗?
Runnable VS Callable两个接口都是用于多线程执行任务的,但他们还是有很明显的差别的
执行机制先从执行机制上来看,Runnable 你太清楚了,它既可以用在 Thread 类中,也可以用在 ExecutorService 类中配合线程池的使用;Bu~~~~t, Callable 只能在 ExecutorService 中使用,你翻遍 Thread 类,也找不到Callable 的身影
异常处理Runnable 接口中的 run 方法签名上没有 throws ,自然也就没办法向上传播受检异常;而 Callable 的 call() 方法签名却有 throws,所以它可以处理受检异常;
所以归纳起来看主要有这几处不同点:
整体差别虽然不大,但是这点差别,却具有重大意义
返回值和处理异常很好理解,另外,在实际工作中,我们通常要使用线程池来管理线程(原因已经在 为什么要使用线程池? 中明确说明),所以我们就来看看 ExecutorService 中是如何使用二者的
ExecutorService先来看一下 ExecutorService 类图
我将上图标记的方法单独放在此处
void execute(Runnable command); <T> Future<T> submit(Callable<T> task); <T> Future<T> submit(Runnable task, T result); Future<?> submit(Runnable task);可以看到,使用ExecutorService 的 execute() 方法依旧得不到返回值,而 submit() 方法清一色的返回 Future 类型的返回值
细心的朋友可能已经发现, submit() 方法已经在 CountDownLatch 和 CyclicBarrier 傻傻的分不清楚? 文章中多次使用了,只不过我们没有获取其返回值罢了,那么
Future 到底是什么呢?
怎么通过它获取返回值呢?
我们带着这些疑问一点点来看
FutureFuture 又是一个接口,里面只有五个方法: