Runnable, Callable, Future, ListenableFuture与FutureTask,例程

Runnable,Callable,Future和FutureTask都是java为实现多线程编程设计的类。

1. 例程

二话不说,先上例程。

1. 实现Runnable接口,无返回值(Thread,线程池)

//直接new Runnable接口,实现全部抽象方法即可(匿名类)
//如果此类需要复用,可以单独定义类,实现Runnable接口
Runnable myProcess = new Runnable() {
    @Override
    public void run() {
        //一些逻辑
    }
};
new Thread(myProcess).start();

2. 实现Callable接口,有返回值(线程池)

//直接new Callable接口,参考上面Runnable信息。
ExecutorService executorService = Executors.newFixedThreadPool(20);
Future<MyClass> future = executorService.submit(new Callable<MyClass>() {
   @Override
    public MyClass call() throws Exception {
        System.out.println("callable start.");
        //一些逻辑
        return myObj.myMethod();
    }
});
//经过一段时间,准备获取运行结果
MyClass result = future.get();

3. 使用FutureTask,有返回值(Thread,线程池)

FutureTask<MyClass> aimFutureTask = new FutureTask<MyClass>(new Callable<MyClass>() {
    @Override
    public MyClass call() throws Exception {
        return someObj.someMethod();
    }
});
new Thread(aimFutureTask).start();
//获取运行结果
MyClass result = aimFutureTask.get();
//使用线程池获取与2类似,但是注意不要使用线程池返回的future,要直接使用futureTask的get()等方法。

2. Runnable和Callable

java中实现多线程编码最基础的做法,是继承Thread类,或实现Runnable接口并传入新建的Thread对象。调用Thread.start()开始执行。(Thread类实现了Runnable接口)
但由于Runnable的设计,无法获取Runnable.run()中定义的业务逻辑的返回值。Callable则很好地解决了这个问题。以下是两者的定义。

//Runnable.java
package java.lang;

public interface Runnable {
    public abstract void run();
}

//Callable.java
package java.util.concurrent;

public interface Callable<V> {
    V call() throws Exception;
}

可见Callable的执行方法call()有返回值并能抛出异常。
Thread并不能接收Callable的对象,仅能接收Runnable类型。
Runnable的实现和Callable的实现,都可以被ExcutorService的实现类执行(如ThreadPoolExecutor或ScheduledThreadPoolExecutor)。
ExcutorService定义了三种方法:

<T> Future<T> submit(Callable<T> task);  
<T> Future<T> submit(Runnable task, T result);  
Future<?> submit(Runnable task);  

其中1方法中,Future.get()可以获得task的返回值;2方法中,Future.get()在task执行完毕后,返回预先传入的result;3方法中,Future.get()在task执行完毕后,返回null。(2和3仅能标志执行完毕,不能真正获取返回值)。

3. Future

3.1 Future

Future是用来对正在执行的线程中的任务(Runnable或Callable)进行控制的类。可以获取执行状态,进行中断,以及最重要的:获取线程返回值

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;
}

方法解析:
V get() :获取异步执行的结果,如果没有结果可用,此方法会阻塞直到异步计算完成。
V get(Long timeout , TimeUnit unit) :获取异步执行结果,如果没有结果可用,此方法会阻塞,但是会有时间限制,如果阻塞时间超过设定的timeout时间,该方法将返回null。
boolean isDone() :如果任务执行结束,无论是正常结束或是中途取消还是发生异常,都返回true。
boolean isCanceller() :如果任务完成前被取消,则返回true。
boolean cancel(boolean mayInterruptRunning) :mayInterruptRunning参数表示是否中断执行中的线程。如果任务还没开始,执行cancel(…)方法将返回false;如果任务已经启动,执行cancel(true)方法将以中断执行此任务线程的方式来试图停止任务,如果停止成功,返回true;当任务已经启动,执行cancel(false)方法将不会对正在执行的任务线程产生影响(让线程正常执行到完成),此时返回false;当任务已经完成,执行cancel(…)方法将返回false。
Future的对象不能通过new Thread().start()获取,只能通过线程池.submit()获得。

3.2 ListenableFuture

对于Future,如果用户想获取其返回值,或者在失败/异常时进行操作,只能依赖主线程轮询future.get()得到。guava提供了ListenableFuture,让Future可以配置回调函数:onSuccess()onFailure() 。当用户不关注线程返回值,只需要线程在成功或失败时执行逻辑,这会是个好的选择。
当然,在一般的业务场景中,最好还是对Future进行get,以保证线程顺利执行。
ListenableFuture详参:http://blog.csdn.net/ligeforrent/article/details/79638063

由上可知,Future的对象不能通过new Thread().start()获取,只能通过线程池.submit()获得。那么问题来了,在没有线程池,又想获得多线程执行结果的情况,怎么办?

4. FutureTask类

FutureTask是对Runnable和Future的实现类。由于前者,它可以提交给Thread(避免线程池)或者线程池执行;由于后者,它拥有对正在执行的线程的与Future相同的操作,如获取返回值,中断线程等。尽管它实现了Runnable接口,但执行线程操作是通过内部的Callable对象实现的多线程操作。

FutureTask使用:

FutureTask有两种构造方法:

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
}

可见可以使用Callable变量,或Runnable变量初始化FutureTask的对象(如futureTask)。
初始化后,将futureTask直接提交给Thread对象执行,或提交给线程池的submit方法,均可。
此后,通过futureTask.get()获取返回值,futureTask.cancel(false)尝试取消,futureTask.cancel(true)强行中断等。(对线程池来说,futureTask是一个Runnable,所以线程池返回的Future将无法获取返回值。详参上述ExcutorService)

FutureTask详解:

FutureTask的状态机

FutureTask用状态机获取线程执行状态,判断是否已执行过,保证幂等性等。
NEW 新建 0
COMPLETING 执行中 1
NORMAL 正常 2
EXCEPTIONAL 异常 3
CANCELLED 取消 4
INTERRUPTING 中断中 5
INTERRUNPED 被中断 6
状态机的转换路径:
NEW -> COMPLETING -> NORMAL
NEW -> COMPLETING -> EXCEPTIONAL
NEW -> CANCELLED
NEW -> INTERRUPTING -> INTERRUPTED
对象新建后,状态机为NEW。
业务执行过程中,FutureTask状态机仍为NEW,但在执行完毕后,会走路径1或2。
而业务不管是否开始执行,调用cancel()方法后,只要状态机仍为NEW,会走路径3或4。

调用futureTask.get()

如果此时currentThread调取执行结果task.get(),会有几种情况:
if task 还没有被executor调度或正在执行中
阻塞当前线程,并加入到一个阻塞链表中waitNode
else if task被其它Thread取消成功或task被中断
throw exception
else if task执行完毕(可能抛过异常)
返回执行结果,或执行存在异常,返回异常信息
代码详解:http://blog.csdn.net/liulipuo/article/details/39029643

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值