@FunctionalInterface
public interface Runnable {
// 这个run()方法返回值为void,没有受检异常的异常声明,出现异常只能内部捕获
public abstract void run();
}
Callable接口源码:
@FunctionalInterface
public interface Callable {
// 这个call()方法有返回值,且声明了受检异常,可以直接抛出Exception异常
V call() throws Exception;
}
Runnable接口和Callable接口的区别:
Runnable接口中的唯一抽象方法run()方法没有返回值,Callable接口中的唯一抽象方法call()方法有返回值;
Runnable接口的run()方法没有受检异常的异常声明,即异常只能在内部捕获,不能继续上抛,Callable接口的call()方法声明了受检异常,可直接抛出Exception异常,并且可以不予捕获;
Callable实例不能和Runnable实例一样,直接作为Thread线程实例的target来使用;
异步执行任务在大多数情况下是通过线程池去提交的,而Runnable接口和Callable接口都可以应用于线程池;
Callable接口支持返回执行结果,此时需要调用FutureTasget()方法实现,此方法会阻塞主线程直到获取‘将来’结果;当不调用此方法时,主线程不会阻塞!
注意:异步执行任务在大多数情况下是通过线程池去提交的,而很少通过创建一个新的线程去提交。
下面实例解析以上不同点:
Runnable接口实例
通过实现Runnable接口的方式创建一个异步执行任务:
public class RunnableTask implements Runnable {
// 线程体:需要线程异步执行的任务
@Override
public void run() {
System.out.println('实现Runnable接口来编写异步执行任务');
// run()方法内出现异常,只能捕获不能直接抛出
try {
Thread.sleep(1000) ;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
方式通过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();
}
}
方式通过线程池创建线程,并提交异步执行任务
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
}
}
Callable接口原理
Callable接口实例没有办法作为Thread线程实例的target来使用。既然如此,那么该如何使用Callable接口创建线程呢?因此需要一个在Callable接口与Thread线程之间起到搭桥作用的重要接口。
RunnableFuture接口源码:
RunnableFuture接口实现了2个目标:
RunnableFuture通过继承Runnable接口,从而保证了其实例可以作为Thread线程实例的target目标;RunnableFuture通过继承Future接口,从而保证了可以获取未来的异步执行结果。
那有些同学可能疑惑了,Future接口又是什么?Future接口至少提供了3大功能:
能够取消异步执行中的任务;判断异步任务是否执行完成;获取异步任务完成后的执行结果;
Future接口的源码:
public interface Future {
// 取消异步执行中的任务
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(Longtimeout,TimeUnitunit):设置时限,阻塞性地获取异步任务执行的结果。该方法的调用也是阻塞性的,但是结果获取线程会有一个阻塞时长限制,不会无限制地阻塞和等待,如果其阻塞时间超过设定的timeout时间,该方法将抛出异常,调用线程可捕获此异常。booleanisDone():获取异步任务的执行状态。如果任务执行结束,就返回true。booleanisCancelled():获取异步任务的取消状态。如果任务完成前被取消,就返回true。booleancancel(booleanmayInterruptRunnin:取消异步任务的执行。
总体来说,Future是一个对异步任务进行交互、操作的接口。但是Future仅仅是一个接口,通过它没有办法直接完成对异步任务的操作,JDK提供了一个默认的实现类——FutureTask。
FutureTask类:
RunnableFuture只是一个接口,无法直接创建对象,如果需要创建对象,就需用到它的实现类——FutureTask。所以说,FutureTask类才是真正的在Thread与Callable之间搭桥的类。FutureTask类实现了RunnableFuture接口,而RunnableFuture接口又继承了Runnable接口和Future接口,因此它可以作为Thread线程实例的target目标,也可以获取异步执行结果;
FutureTask内部有一个Callable类型的成员——callable实例属性:
callable实例属性用来保存并发执行的Callable类型的任务,并且callable实例属性需要在FutureTask实例构造时进行初始化:
public FutureTask(Callable 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 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实例属性:
FutureTask的outcome实例属性用于保存callable成员call()方法的异步执行结果。在FutureTask类的run()方法完成callable成员的call()方法的执行之后,其结果将被保存在outcome实例属性中,供FutureTask类的get()方法获取。
Callnable接口实例
通过实现Runnable接口的方式创建一个异步执行任务:
public class CallableTaskDemo implements Callable {
// call()方法有返回值,并且可以抛出Exception异常
@Override
public String call() throws Exception {
System.out.println('实现Callable接口来编写异步执行任务');
Thread.sleep(1000);
return '返回线程执行结果';
}
}
方式通过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());// 获取异步执行任务结果:返回线程执行结果
}
}
方式通过线程池创建线程,并提交异步执行任务:
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());
}
}
FutureTask是什么?
Future是一个对异步任务进行交互、操作的接口。但是Future仅仅是一个接口,通过它没有办法直接完成对异步任务的操作,JDK提供了一个默认的实现类——FutureTask。
对于Calleble来说,Future和FutureTask均可以用来获取任务执行结果,不过Future是个接口,FutureTask是Future的具体实现,而且FutureTask还间接实现了Runnable接口,也就是说FutureTask可以作为Runnable任务提交给线程池。
线程池中submit()和execute()方法有什么区别?
两者都是将一个线程任务添加到线程池中并执行;excutor没有返回值,submit有返回值,并且返回执行结果Future对象excutor不能提交Callable任务,只能提交Runnable任务,submit两者任务都可以提交在submit中提交Runnable任务,会返回执行结果Future对象,但是Future调用get方法将返回null
文章为作者独立观点,不代表 股票程序化软件自动交易接口观点
一剑双雕2023-06-30
马克陈,我一直潜水,没有发表任何言论,但我买的兴业银行股票超过一百万股,由少变多已经持有六年了,方便的话拉我入去高端群大家一起分享一下。