文章目录
- 1. Runnable接口实例
- 2. Callable接口原理
- 3. Callnable接口实例
- 4. FutureTask是什么?
- 5. 线程池中 submit() 和 execute() 方法有什么区别?
Runnable接口源码:
@FunctionalInterface
public interface Runnable {// 这个run()方法返回值为void,没有受检异常的异常声明,出现异常只能内部捕获public abstract void run();
}
Callable接口源码:
@FunctionalInterface
public interface Callable<V> {// 这个call()方法有返回值,且声明了受检异常,可以直接抛出Exception异常V call() throws Exception;
}
Runnable接口和Callable接口的区别:
(1) Runnable接口中的唯一抽象方法run()方法没有返回值,Callable接口中的唯一抽象方法call()方法有返回值;
(2) Runnable接口的run()方法没有受检异常的异常声明,即异常只能在内部捕获,不能继续上抛, Callable接口的call()方法声明了受检异常,可直接抛出Exception异常,并且可以不予捕获;
(3) Callable实例不能和Runnable实例一样,直接作为Thread线程实例的target来使用;
(4) 异步执行任务在大多数情况下是通过线程池去提交的,而Runnable接口和Callable接口都可以应用于线程池;
(5) Callable接口支持返回执行结果,此时需要调用FutureTask.get()
方法实现,此方法会阻塞主线程直到获取‘将来’结果;当不调用此方法时,主线程不会阻塞!
注意:异步执行任务在大多数情况下是通过线程池去提交的,而很少通过创建一个新的线程去提交(即通过Thread类的方式执行start()方法)。
下面实例解析以上不同点:
1. Runnable接口实例
首先,通过实现Runnable接口的方式创建一个异步执行任务:
public class RunnableTask implements Runnable {// 线程体:需要线程异步执行的任务@Overridepublic void run() {System.out.println("实现Runnable接口来编写异步执行任务");// run()方法内出现异常,只能捕获不能直接抛出try {Thread.sleep(1000) ;} catch (InterruptedException e) {e.printStackTrace();}}
}
方式1:通过Thread类创建线程,将Runnable接口的实现类作为Thread线程实例的target来使用,执行异步任务
public class RunnableDemo {public static void main(String[] args) {Runnable target = new RunnableTask();// 通过Thread类创建一个新线程,并启动Thread thread = new Thread(target);thread.start();}
}
方式2:通过线程池创建线程,并提交异步执行任务
public class RunnableDemo2 {// 通过线程池创建3个线程private static ExecutorService executorService = Executors.newFixedThreadPool(3);public static void main(String[] args) throws ExecutionException, InterruptedException {// 1、通过线程池的execute()方法提交异步执行任务,没有返回结果executorService.execute(new RunnableTaskDemo());// 2、通过线程池的submit()方法提交异步执行任务,有返回结果Future<?> submit = executorService.submit(new RunnableTaskDemo());// 获取返回结果System.out.println(submit.get()); // null}
}
2. Callable接口原理
Callable接口实例没有办法作为Thread线程实例的target来使用。既然如此,那么该如何使用Callable接口创建线程呢?因此需要一个在Callable接口与Thread线程之间起到搭桥作用的重要接口。
1、RunnableFuture接口源码:
public interface RunnableFuture<V> extends Runnable, Future<V> {void run();
}
RunnableFuture接口实现了2个目标:
(1) RunnableFuture通过继承Runnable接口,从而保证了其实例可以作为Thread线程实例的target目标;
(2) RunnableFuture通过继承Future接口,从而保证了可以获取未来的异步执行结果。
那有些同学可能疑惑了,Future接口又是什么?Future接口至少提供了3大功能:
(1) 能够取消异步执行中的任务;
(2) 判断异步任务是否执行完成;
(3) 获取异步任务完成后的执行结果;
2、Future接口的源码:
public interface Future<V> {// 取消异步执行中的任务boolean cancel(boolean mayInterruptIfRunning);boolean isCancelled();// 判断异步任务是否执行成功boolean isDone();// 获取异步任务完成后的结果V get() throws InterruptedException, ExecutionException;V get(long timeout, TimeUnit unit)throws InterruptedException, ExecutionException, TimeoutException;
}
Future接口中的方法:
-
get():获取异步任务执行的结果。注意,这个方法的调用是阻塞性的。如果异步任务没有执行完成,异步结果获取线程(调用线程)会一直被阻塞,一直阻塞到异步任务执行完成,其异步结果返回给调用线程。
-
get(Long timeout,TimeUnit unit):设置时限,(调用线程)阻塞性地获取异步任务执行的结果。该方法的调用也是阻塞性的,但是结果获取线程(调用线程)会有一个阻塞时长限制,不会无限制地阻塞和等待,如果其阻塞时间超过设定的timeout时间,该方法将抛出异常,调用线程可捕获此异常。
-
boolean isDone():获取异步任务的执行状态。如果任务执行结束,就返回true。
-
boolean isCancelled():获取异步任务的取消状态。如果任务完成前被取消,就返回true。
-
boolean cancel(boolean mayInterruptRunning):取消异步任务的执行。
总体来说,Future是一个对异步任务进行交互、操作的接口。但是Future仅仅是一个接口,通过它没有办法直接完成对异步任务的操作,JDK提供了一个默认的实现类——FutureTask。
3、FutureTask类 :
RunnableFuture只是一个接口,无法直接创建对象,如果需要创建对象,就需用到它的实现类——FutureTask。所以说,FutureTask类才是真正的在Thread与Callable之间搭桥的类。FutureTask类实现了RunnableFuture接口,而RunnableFuture接口又继承了Runnable接口和Future接口,因此它可以作为Thread线程实例的target目标,也可以获取异步执行结果;
public class FutureTask<V> implements RunnableFuture<V> {}
FutureTask内部有一个Callable类型的成员——callable实例属性:
private Callable<V> callable;
callable实例属性用来保存并发执行的Callable类型的任务,并且callable实例属性需要在FutureTask实例构造时进行初始化:
public FutureTask(Callable<V> callable) {if (callable == null) throw new NullPointerException();// callable实例属性需要在FutureTask实例构造时进行初始化this.callable = callable;this.state = NEW; // ensure visibility of callable
}
public FutureTask(Runnable runnable, V result) {this.callable = Executors.callable(runnable, result);this.state = NEW; // ensure visibility of callable
}
FutureTask类实现了Runnable接口,在其run()方法的实现版本中会执行callable成员的call()方法:
public void run() {if (state != NEW ||!UNSAFE.compareAndSwapObject(this, runnerOffset, null, Thread.currentThread()))return;try {Callable<V> c = callable;if (c != null && state == NEW) {V result;boolean ran;try {// 在run()方法中执行call()方法result = c.call();ran = true;} catch (Throwable ex) {// ...}if (ran) set(result);}} finally {// ...}
}
FutureTask内部还有另一个非常重要的Object类型的成员——outcome实例属性:
private Object outcome;
FutureTask的outcome实例属性用于保存callable成员call()方法的异步执行结果。在FutureTask类的run()方法完成callable成员的call()方法的执行之后,其结果将被保存在outcome实例属性中,供FutureTask类的get()方法获取。
3. Callnable接口实例
首先,通过实现Runnable接口的方式创建一个异步执行任务:
public class CallableTaskDemo implements Callable {// call()方法有返回值,并且可以抛出Exception异常@Overridepublic String call() throws Exception {System.out.println("实现Callable接口来编写异步执行任务");Thread.sleep(1000);return "返回线程执行结果";}
}
方式1:通过Thread类创建线程执行异步任务
public class CallableDemo {public static void main(String[] args) throws ExecutionException, InterruptedException {// 创建异步执任务实例Callable callable = new CallableTaskDemo();// 初始化callable实例属性FutureTask futureTask = new FutureTask(callable);// 创建线程执行异步任务Thread thread = new Thread(futureTask);thread.start();System.out.println("获取异步执行任务结果:"+futureTask.get());// 获取异步执行任务结果:返回线程执行结果}
}
方式2:通过线程池创建线程,并提交异步执行任务:
public class CallableDemo2 {// 通过线程池创建3个线程private static ExecutorService executorService = Executors.newFixedThreadPool(3);public static void main(String[] args) throws ExecutionException, InterruptedException {// 通过线程池的submit()方法提交异步执行任务,有返回结果Future submit = executorService.submit(new CallableTaskDemo());// 获取返回结果System.out.println(submit.get());}
}
4. FutureTask是什么?
Future是一个对异步任务进行交互、操作的接口。但是Future仅仅是一个接口,通过它没有办法直接完成对异步任务的操作,JDK提供了一个默认的实现类——FutureTask。
对于Calleble来说,Future和FutureTask均可以用来获取任务执行结果,不过Future是个接口,FutureTask是Future的具体实现,而且FutureTask还间接实现了Runnable接口,也就是说FutureTask可以作为Runnable任务提交给线程池。
5. 线程池中 submit() 和 execute() 方法有什么区别?
两者都是将一个线程任务添加到线程池中并执行;
1、excutor没有返回值,submit有返回值,并且返回执行结果Future对象
2、excutor不能提交Callable任务,只能提交Runnable任务,submit两者任务都可以提交
3、在submit中提交Runnable任务,会返回执行结果Future对象,但是Future调用get方法将返回null(Runnable没有返回值)