Future、FutureTask、Callable、Runnable、Thread

  2019-6-20 


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

待进一步深层源码解读

参考:FutureTask原理解析


且听风吟