Future、FutureTask、Callable在JUC包中,Runnable和Thread在java.lang中
Runnable接口
顶级接口,线程执行体
必须实现run()
方法,即为任务
Callable接口
顶级接口,线程执行体
必须实现call()
方法,即为任务,且在该方法中return返回值
任务的执行
Runnable、Callable、FutureTask是定义任务,并不会新建线程!执行任务需要新建线程
线程管理需要:
①Thread:可以a.由Thread实现run b. 或传入Runnable,由Thread负责创建线程(Thread.start()),然后内部会调用run()。eg:new Thread(new myRun)、或继承Thread重写run()
②线程池:也可以使用线程池的submit()
,其中直接传入Runnable、Callable或FutureTask,由线程池负责线程的管理(创建、销毁等)。eg:ExecutorService = Executors.newCachedThreadPool();
线程池取代Thread进行线程的管理
P.S. 注意Thread执行完任务后,线程就自动销毁了;而线程池不一定,不同的线程池管理线程的方式不一样,线程池一般会线程复用,执行完任务线程会继续运行(程序未终止)
Thread
实现Runnable并继承Object
Thread是抽象类
必须实现其中的run()
方法,也可以直接new Thread(Runnable r)
来构造Thread对象
我们在想要新建线程执行run()方法的时候,就要myThread.start()
start()是准备就绪,当线程可以执行的时候才会调用run方法。
【重要】当程序调用start()方法时,会创建一个新线程,然后执行run()方法。
但是如果我们直接调用run()方法,则不会创建新的线程,run()方法将作为当前调用线程本身的常规方法调用执行,并且不会发生多线程。
Runnable任务可以共享数据,多个Thread可以同时加载一个Runnable实现类实例,当各自Thread获得CPU时间片的时候开始运行runnable实现类实例,runnable实现类实例里面的资源是被共享的
Runnable和Callable只提供了run()/call()方法,无法任务控制,也无法异步获取Callable的返回结果,所以引出牛逼哄哄的FutureTask
Future接口
Future接口定义了一系列任务控制的方法,并且定义了获取异步线程的返回值的方法
//Future接口
public interface Future<V>
{
//mayInterruptRunning参数表示是否中断执行中的线程
//①若中断(mayInterruptRunning=true)
//如果任务还没开始,返回false;
//如果任务已经启动,将以中断执行此任务线程的方式来试图停止任务,如果停止成功,返回true;
//②若不中断(mayInterruptRunning=false)
//当任务已经启动,将不会对正在执行的任务线程产生影响(让线程正常执行到完成),此时返回false;
//当任务已经完成,返回false。
boolean cancel(boolean mayInterruptIfRunning);
//如果任务完成前被取消,则返回true
boolean isCancelled();
//如果任务执行结束,无论是正常结束或是中途取消还是发生异常,都返回true
boolean isDone();
//获取异步执行的结果,如果没有结果可用,此方法会阻塞直到异步计算完成
//对于使用Runnable构造器,调用该方法会返回null
V get() throws InterruptedException, ExecutionException;
//获取异步执行结果,如果没有结果可用,此方法会阻塞,但是会有时间限制,如果阻塞时间超过设定的timeout时间,该方法将抛出异常
V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
}
FutureTask
FutureTask实现了RunnableFuture接口,RunnableFuture接口继承了Runnable接口和Future接口(即可以通过Runnable接口实现线程(实际上是一律将Runnable转化为Callable来处理),也可以通过Future取得线程执行完后的结果以及使用一系列的线程控制方法),因此FutureTask也可以直接提交给Executor执行。
【FutureTask是为了弥补Runnable和Callable的不足而设计的】:
①可以获得异步返回结果(如果需要)
②提供了一系列方法方便线程控制以及状态查看(Runable方法只有一个run(),只能调用Object的wait等方法进行线程控制,不好)
FutureTask有两个构造器,对应上面两点
①FutureTask(Runnable runnable, V result)
:无返回值异步运行,它在Runable的接口之上,封装了很多方法(对于Runable来说,FutureTask就像一个装饰器,实现Future接口来装饰Runable)
②FutureTask(Callable<V> callable)
:在①的基础上,变为有返回值异步运行结果
【ATT】FutureTask.run()的run()方法(就是Runnable的run方法)并没有创建线程,所以得交由线程池来执行
Runnable、Callable、FutureTask、Future使用例
class myRun implements Runnable
{
public void run() //runnable实现run方法
{
System.out.println("123");
}
}
class myCall implements Callable
{
public String call() //Callable里实现call方法
{
return "123";
}
}
public class main
{
public static void main(String[] args) throws Exception
{
/*新建线程池,负责线程的管理(创建、销毁等)*/
ExecutorService pool = Executors.newCachedThreadPool();
/*调用FutureTask(Runnable runnable, V result)构造器,作为封装的Runnable使用*/
//第二个参数是为了兼容Excutors.callable方法(见PS),直接设置为null即可
FutureTask ft = new FutureTask(new myRun(),null);
pool.submit(ft);
/*调用FutureTask(Callable<V> callable)构造器,使用Future功能*/
FutureTask<T> ft = new FutureTask(new myCall());
//使用线程池的submit方法提交该FutureTask之后,异步运算结果保存在FutureTask自己中
pool.submit(ft);
/*线程池的submit方法返回的就是个FutureTask实例,我们只是一般用Future接口来声明其引用*/
Future<String> ft = pool.submit(new myCall());
/*然后可以调用FutureTask的各种方法了*/
//比如<T> temp = ft.get(); isDone()等
}
}
P.S. Executors.callable
用来把Runnable
包装成Callable<T>
。包装出来的Callable<T>
只能返回传入的result
Callable<T> Executors.callable(Runnable task, T result)
使用场景:当将需要Callable的方法应用于其他无结果的操作时
What’s next【】
state属性是贯穿整个FutureTask的最核心的属性,该属性的值代表了任务在运行过程中的状态,随着任务的执行,状态将不断地进行转变。一共有7个state状态,都是volatile的,保证所有线程可见性。
FutureTask五大核心属性:
任务本尊:Callable //如果构造器传入的是Runnable,也会被转化为Callable
任务的执行者:runner
任务的结果:outcome
获取任务的结果:state + outcome + waiters
中断或者取消任务:state + runner + waiters
任务本尊:Callable //如果构造器传入的是Runnable,也会被转化为Callable
任务的执行者:runner
任务的结果:outcome
获取任务的结果:state + outcome + waiters
中断或者取消任务:state + runner + waiters
待进一步深层源码解读