对于多个在逻辑上互不依赖的耗时任务而言,并行化执行效率要比串行化执行效率高。对于这些任务往往在其执行完毕后要取得任务的执行结果。Callable
和FutureTask
就实现了这一机制。
Callable和Runnable区别
区别
- callable可以携带返回值,泛型
- callable可以抛出异常
- callable的方法名是call,runnable方法名是run
联系
- callable本质上还是要在runnable内执行
Callable和FutureTask成对出现
runnable
封装了任务的具体逻辑,要进行多线程编程,需要Thraed来执行
callable
封装了任务的具体逻辑,但它不能被Thread
直接执行,因为Thread
的构造参数中就只有runnable
,所以在callable
和ThreadTask
中还有FutureTask
,那么FutureTask
有什么作用?
FutureTask
FutureTask的本质就是一个runnable。
- 是runnable的实现
- 它的构造参数接受callable
- 并且有泛型定义,记录任务的执行结果
- get方法可以获取callable任务的执行结果,如果任务执行完直接返回结果,任务还没执行完,调用线程会阻塞线程直至get返回结果
基于以上的特点实现一个简单的FutureTask。
public class AyFutureTask<T> implements Runnable
{
/* 任务封装 */
Callable<T> callable;
/* 存放任务执行结果 */
T result;
/* 记录当前任务的执行状态 */
volatile String state = "NEW";
/* 记录阻塞的线程 */
Thread thread;
public AyFutureTask(Callable<T> callable){
this.callable = callable;
}
@Override
public void run()
{
try
{
result = callable.call();
} catch (Exception e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
state = "END";
//唤醒阻塞线程
LockSupport.unpark(thread);
}
}
T get(){
if(!state.equals("END")){
//线程等待,不使用wait和notify的原因是因为如果nitofy比wait执行的早,
//那么wait将一直等待下去,而使用park/unpark则无此问题
thread = Thread.currentThread();
LockSupport.park();
}
return result;
}
}
使用例子
public class FutureTaskTest
{
public static void main(String[] args) throws InterruptedException, ExecutionException
{
long now = System.currentTimeMillis();
System.out.println("开始测试:"+now);
Callable<String> call1 = new Callable<String>()
{
@Override
public String call() throws Exception
{
//1.第一部分任务
Thread.sleep(3000);
//System.out.println("获得第一步信息");
return "第一部分结果";
}
};
Callable<String> call2 = new Callable<String>()
{
@Override
public String call() throws Exception
{
//2.第二部分任务
Thread.sleep(2000);
//System.out.println("获得第二步信息");
return "第二部分结果";
}
};
AyFutureTask<String> task1 = new AyFutureTask<String>(call1);
AyFutureTask<String> task2 = new AyFutureTask<String>(call2);
Thread t1 = new Thread(task1);
Thread t2 = new Thread(task2);
t1.start();
t2.start();
//get会等待线程
System.out.println(task1.get());
System.out.println(task2.get());
System.out.println("结束测试:"+(System.currentTimeMillis() - now));
}
}