1.线程创建
- 我们创建线程一般有以下几种方式:
1,继承Thread,重写run方法
2,实现Runnable接口,重新run方法
3,实现Callable接口并重写call方法。
4,使用线程池,详见:【JUC】线程池-ThreadPoolExecutor源码解析
- 我们今天聊的就是第三种。实现Callable接口并重写call方法,这种方式与 Runnable 相比Callable又有些不同:
1, Callable 可以在任务结束的时候提供一个返回值,Runnable 无法提供这个功能
2,Callable 的 call 方法分可以抛出异常,而 Runnable 的 run 方法不能抛出异常。
- 接下来我们来看看Callable 又是怎样实现的。
2.Callable 实现原理
- 在聊Callable 之前我们先回想一下Runnable是怎么用的,下面是个小Demo,我们先看看。
- Demo:
/**
* Runnable使用demo
*
* @author wangjie
* @version V1.0
* @date 2019/12/25
*/
@Slf4j
public class RunnableDemo {
public static void main(String[] args) {
Thread thread = new Thread(new RunnableResource());
thread.start();
}
}
/**
* 线程
*/
@Slf4j
class RunnableResource implements Runnable{
@Override
public void run() {
log.info("【这是个Runable使用demo】");
}
}
- Demo太简单,就不贴运行结果了,我们主要看的是Runnable是怎么用的。
- 显而易见,Runnable的使用是作为Thread的构造参数,来开启新线程的。
- 我们再来看看Thread在JDK8文档中的API
- 结论:Thread的构造方法中,只有Runnable作为构造参数的构造方法,没有Callable作为构造参数的构造方法,以往的Runnable使用经验无法直接套用。
- 那Callable到底是怎么用的呢?还是直接来个小Demo:
/**
* Runnable使用demo
*
* @author wangjie
* @version V1.0
* @date 2019/12/25
*/
@Slf4j
public class CallableDemo {
public static void main(String[] args) {
//main线程开始时间
Long startTime = System.currentTimeMillis();
FutureTask<Integer> futureTask = new FutureTask(new CallableResource());
Thread thread = new Thread(futureTask);
thread.start();
int i = 1;
Integer o = null;
try {
log.info("【这是个main线程,后续运算,预计耗时2秒钟】");
TimeUnit.SECONDS.sleep(3);
o = futureTask.get();
} catch (Exception e) {
e.printStackTrace();
}
log.info("【i+0:】{}",o+i);
log.info("main总耗时{}ms",(System.currentTimeMillis()-startTime));
}
}
/**
* 线程
*/
@Slf4j
class CallableResource implements Callable<Integer>{
@Override
public Integer call() throws Exception {
log.info("【这是个Callable使用demo,开始运算,预计耗时3秒钟】");
TimeUnit.SECONDS.sleep(3);
return 1;
}
}
- 运行结果:
[main] INFO com.test.callable.CallableDemo - 【这是个main线程,后续运算,预计耗时2秒钟】
[Threa