与多线程相关的方法—-Callable,Future和FutureTask
除了Runnable之外,Java中还有Callable,Futrue,FutureTask这几个与多线程相关的概念,与Runnable不同的是这几个类都只能运用到线程池中,而Runnable既能运用到Thread中,又能运用到线程池中。
Callable
Callable与Runnable的功能大致相似,不同的是Callable是一个泛型接口,它有一个泛型参数V,该接口中有一个返回值(类型为V)的call()
方法,而Runnable的run()
方法不能将结果返回:
public interface Callable<V> {
V call() throws Exception;
}
Future
Future
为线程池指定了一个可管理的任务标准。它提供了对Runnable或者Callable任务的执行结果进行取消,查询是否完成,获取结果,设置结果操作:
public interface Future<V> {
/**
尝试去取消这个任务的执行,但是这个尝试有可能失败,当这个任务已经完成或者取消的时候,或者因为其他原因。
如果该任务已经开始执行了,将由mayInterruptIfRunning这个参数来决定是否打断任务的执行。
*
*/
boolean cancel(boolean mayInterruptIfRunning);
/**
该任务是否取消
* */
boolean isCancelled();
/**
* Returns {@code true} if this task completed.
* 该任务是否完成
**/
boolean isDone();
/**
获取结果,如果任务未完成,则等待,直到完成。因此该方法会阻塞
**/
V get() throws InterruptedException, ExecutionException;
/**
获取结果,如果任务未完成,直到timeout或者返回结果。该方法会阻塞
* */
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
FutureTask
在上面我们可以看到,Future只是定义了一些规范的接口,而FutureTask是它的实现类。
FutureTask实现了RunnableFture<V>
,而RunnableFuture又实现了Runnable
和Future<V>
,因此FutureTaske具备它们的能力。
public class FutureTask<V> implements RunnableFuture<V>
public interface RunnableFuture<V> extends Runnable, Future<V> {
/**
* Sets this Future to the result of its computation
* unless it has been cancelled.
* */
void run();
}
FutureTask
会像Thread
包装Runnable
一样,对Callable
和Runnable
进行包装:
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
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
}
我们可以看到,当传入Runnable的时候,会用Executors.callable方法转换成Callable类型,即FutureTask最终执行的都是Callable类型的任务:
public static <T> Callable<T> callable(Runnable task, T result) {
if (task == null)
throw new NullPointerException();
return new RunnableAdapter<T>(task, result);
}
private static final class RunnableAdapter<T> implements Callable<T> {
private final Runnable task;
private final T result;
RunnableAdapter(Runnable task, T result) {
this.task = task;
this.result = result;
}
public T call() {
task.run();
return result;
}
}
我们可以看到在用Runnable构造FutureTask的时候,会将Runnable包装为一个RunnableAdapter,在它的Call方法中,其实是执行的Runnable的run方法,但是返回是我们在构造FutureTask的时候传入的result,如果我们不是特别需要它,可以传入null。
了解基本概念后,通过代码来演示Runnable,Callable,FutureTask的运用:
public class FutureDemo {
//线程池
static ExecutorService mExecutor = Executors.newSingleThreadExecutor();
public static void main(String[] args){
try {
futureWithRunnable();
futureWithCallable();
futureTask();
}catch(Exception e){
}
}
//向线程池中提交Runnable对象
private static void futureWithRunnable()throws InterruptedException,ExecutionException{
Future<?> result = mExecutor.submit(new Runnable() {
@Override
public void run() {
fibc(20);
}
});
System.out.println("future result form runnable : "+result.get() );
}
//提交Callable对象,有返回值
private static void futureWithCallable()throws InterruptedException,ExecutionException{
Future<Integer> result2 = mExecutor.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
return fibc(20);
}
});
System.out.println("future result form callable : "+result2.get());
}
//提交FutureTask对象
private static void futureTask()throws InterruptedException,ExecutionException{
FutureTask<Integer> futureTask = new FutureTask<>(
new Callable<Integer>() {
@Override
public Integer call() throws Exception {
return fibc(20);
}
}
);
mExecutor.submit(futureTask);
System.out.println("future result form futureTask : "+futureTask.get());
}
private static int fibc(int num){
if(num ==0){
return 0;
}
if(num == 1){
return 1;
}
return fibc(num-1)+fibc(num-2);
}
}
执行的结果:
future result form runnable : null
future result form callable : 6765
future result form futureTask : 6765
在futureWithRunnable方法中提交了一个Runnable对象,在run方法中进行计算,该方法没有返回值,因此通过Future对象的get函数得到的值为null。
Callable实现的是V call方法,将Callable对象提交给线程池后会返回一个Future对象,通过该对象可以对任务进行取消,获取结果等操作。
FutrueTask是一个RunnableFuture,因此即实现了Runnable又实现了Future这两个接口,它可以包装Runnable(实际上会转为Callable)和Callable,提交给ExecuteService来执行后,也可以通过Future对象的get方法来得到结果。
那么这里面是如何运转的呢?不急,先要知道其他知识
线程池
当我们需要频繁创建多个线程进行耗时操作时,每次都通过new Thread实现并不是一种好方式,每次new Thread新建和销毁对象性能较差,线程缺乏统一的管理,可能无限制新建线程,相互之间竞争,可能占用过多系统资源导致死锁,并且缺乏定时执行,定期执行,线程中断等功能。
java通过Executor提供了四种线程池:
newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。—->ThreadPoolExecutor
newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。—->ThreadPoolExecutor
newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。—->ScheduledThreadPoolExecutor
newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。—->ThreadPoolExecutor
优点:
- 重用存在的线程,减少对象创建,销毁的开销
- 可有效控制最大量并发线程数,提供系统资源的使用率,同时避免过多资源竞争,避免阻塞。
- 提供定时执行,定期执行,单线程,并发控制等功能。
线程池原理的简单解释就是会创建多个线程并且进行管理,提交给线程的任务会被线程池指派给其中的线程进行执行,通过线程池的统一调度,管理使得多线程的使用更简单,高效。
线程池都实现了ExecutorService接口,该接口定义了线程池需要实现的接口,如submit,execute,shutdown等。它的实现有ThreadPoolExecutor和ScheduledPoolExcutor。ThreadPoolExecutor是运用最多的线程池实现,ScheduledThreadPoolExecutor则用于周期性地执行任务。通常,我们都不会直接通过new的形式来创建线程池,由于常见参数过程相对复杂,因为JDK给我们提供了Executors工厂类来简化这份过程。
线程池的类图:
ExecutorService
ExecutorService的声明周期包括3中状态:运行,关闭,终止。创建后便进入到了运行状态。当调用shutdown()方法时,便进入到了关闭状态,此时意味着ExecutorService不再接受新的任务,但它还在执行已经提交了的任务。当所有已经提交了的任务完成后,就变成了终止状态。
public interface ExecutorService extends Executor {
void shutdown();
boolean isShutdown();
boolean isTerminated();
boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException;
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException;
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException;
<T> T invokeAny(Collection<? extends Callable<T>> tasks)
throws InterruptedException, ExecutionException;
<T> T invokeAny(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
//用来执行被提交的任务,也就是一个运行新任务的简单接口
public interface Executor {
/**
* Executes the given command at some time in the future. The command
* may execute in a new thread, in a pooled thread, or in the calling
* thread, at the discretion of the {@code Executor} implementation.
*
* @param command the runnable task
* @throws RejectedExecutionException if this task cannot be
* accepted for execution
* @throws NullPointerException if command is null
*/
void execute(Runnable command);
}
启动指定数量的线程:ThreadPoolExecutor
ThreadPoolExecutor是线程池的实现之一,它的功能时启动指定数量的线程以及将任务添加到一个队列中,并且将任务分发给空闲的线程。
它有四种构造方法:
public ThreadPoolExecutor(int corePoolSize,