7.1、Calledable接口与Runnable接口的区别
- 是否有返回值
Calledable 有返回值;Runnable无返回值
- 是否抛出异常
Calledable 会抛出异常;Runnable不会抛出异常
- 实现方法名称不同
Calledable 实现的是call方法;Runnable实现的是run方法
7.2、Calledable接口与Runnable接口创建线程
package com.ae.juc.callable;import java.util.concurrent.Callable;//实现Runnable接口 class MyThread1 implements Runnable{@Overridepublic void run() {} }//实现Callable接口 class MyThread2 implements Callable {@Overridepublic Object call() throws Exception {return 200;} }public class Demo1 {public static void main(String[] args) {//Runnable创建线程new Thread(new MyThread1(),"AA").start();//Callable创建线程(报错)new Thread(new MyThread2(),"BB").start();} }
7.3、为什么Callable放入Thread的构造器中报错呢?
因为在Thread类的构造器中,根本没有一个是可以传入Callable接口的。都是需要传入Runnable接口。为了要实现Callable接口能干创建线程,我们需要找一个中间类,它既与Runnable接口有关系,同时又与Callable接口有关系,那这个类就是 FutureTask。
7.3、使用FutureTask来实现Callable创建线程
package com.ae.juc.callable;import java.util.concurrent.*;//实现Runnable接口 class MyThread1 implements Runnable{@Overridepublic void run() {System.out.println("----Runnable");} }//实现Callable接口 class MyThread2 implements Callable {@Overridepublic Object call() throws Exception {System.out.println("----Callable");return 200;} }public class Demo1 {public static void main(String[] args) throws ExecutionException, InterruptedException {//Runnable创建线程new Thread(new MyThread1(),"AA").start();//Callable创建线程(报错)//new Thread(new MyThread2(),"BB").start();//使用FutureTask来实现Callable创建线程FutureTask<Integer> futureTask1 = new FutureTask<Integer>(new MyThread2());//lam表达式FutureTask<Integer> futureTask2 = new FutureTask<Integer>(() -> {TimeUnit.SECONDS.sleep(5);return 200;});new Thread(futureTask2,"BB").start();Integer res = futureTask2.get();//阻塞方法System.out.println(res);} }
7.4、Callable创建线程的步骤如下
- 创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,且该call()方法有返回值,创建Callable实现类的实例。从Java 8开始,可以直接使用Lambda表达式创建Callable对象。
- 使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的cal1()方法的返回值。
- 使用FutureTask对象作为Thread对象的target创建并启动新线程。
- 调用FutureTask对象的get()方法来获得子线程执行结束后的返回值。