Java并发编程:ThreadPoolExecutor + Callable + Future(FutureTask) 探知线程的执行状况

如题 (总结要点)

  • 使用ThreadPoolExecutor来创建线程,使用Callable + Future 来执行并探知线程执行情况;
  • V get (long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException 同上面的get功能一样,多了设置超时时间。参数timeout指定超时时间,uint指定时间的单位,在枚举类TimeUnit中有相关的定义。如果计算超时,将抛出TimeoutException。
  • 可以转换为FutureTask: FutureTask task = (FutureTask) poolExecutor.submit(new MyRunner(500));
  • 毕竟:class FutureTask implements RunnableFuture,interface RunnableFuture extends Runnable, Future;
  • FutureTask可以作为线程扔到线程池中运行,并且还可以像下面的Future一样探知线程的执行情况。
  • 下面的线程池poolExecutor.submit 返回的是interface RunnableFuture extends Runnable, Future (查看源码可知):
    public <T> Future<T> submit(Runnable task, T result) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task, result);
execute(ftask);
return ftask;
}

借鉴学习文章列表

1.主题

Future接口提供方法来检测任务是否被执行完,等待任务执行完获得结果,也可以设置任务执行的超时时间。这个设置超时的方法就是实现Java程序执行超时的关键。

Future接口是一个泛型接口,严格的格式应该是Future<V>,其中V代表了Future执行的任务返回值的类型。 Future接口的方法介绍如下:

boolean cancel (boolean mayInterruptIfRunning) 取消任务的执行。参数指定是否立即中断任务执行,或者等等任务结束
boolean isCancelled () 任务是否已经取消,任务正常完成前将其取消,则返回 true
boolean isDone () 任务是否已经完成。需要注意的是如果任务正常终止、异常或取消,都将返回true
V get () throws InterruptedException, ExecutionException 等待任务执行结束,然后获得V类型的结果。InterruptedException 线程被中断异常, ExecutionException任务执行异常,如果任务被取消,还会抛出CancellationException
V get (long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException 同上面的get功能一样,多了设置超时时间。参数timeout指定超时时间,uint指定时间的单位,在枚举类TimeUnit中有相关的定义。如果计算超时,将抛出TimeoutException
Future的实现类有java.util.concurrent.FutureTask<V>即 javax.swing.SwingWorker<T,V>。通常使用FutureTask来处理我们的任务。FutureTask类同时又实现了Runnable接口,所以可以直接提交给Executor执行。

2. 代码

/**
* 测试子线程,计算100以内的整数和
*/ class MyRunner implements Callable<Integer>{
private int sleepTime ; public MyRunner(int sleepTime) {
this.sleepTime = sleepTime;
} Logger logger = Logger.getLogger("myRunner");
@Override
public Integer call() throws Exception {
logger.info("子线程开始运行");
Thread.sleep(sleepTime);
int sum = 0;
for(int i=1;i<=100;i++){
sum += i;
}
logger.info(sleepTime/1000.0+"s后,子线程结束运行.100以内的正数和为:"+sum);
return sum;
}
}

3.测试 主线程启动

import java.util.concurrent.*;
import java.util.logging.Logger; /** https://www.cnblogs.com/dolphin0520/p/3949310.html
* 《java并发编程的艺术》
*/
public class Test {
private static Logger logger = Logger.getLogger("Test");
public static void main(String[] args) {
/**
* 测试Callable 接口
* 这是一个泛型接口,call()函数返回的类型就是传递进来的V类型
* Callable一般是和ThreadPoolExecutor配合来使用的
* 使用futureTask
*/
BlockingQueue<Runnable> queue = new SynchronousQueue<>();
ThreadFactory nameThreadFactory = new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
return new Thread(r);
}
};
ThreadPoolExecutor poolExecutor =
new ThreadPoolExecutor(2, 2, 0, TimeUnit.MILLISECONDS, queue, nameThreadFactory); /**
* 将实现Callable 或者runnable 接口的类提交给线程池即可
* */
Future<Integer> task = poolExecutor.submit(new MyRunner(500)); poolExecutor.shutdown(); try {
task.get(100,TimeUnit.MILLISECONDS);
logger.info("打印submit执行结果?是否done?");
boolean done = task.isDone();
logger.info(String.valueOf(done));
// 如果没有完成,取消当前线程的运行
if(!done){
task.cancel(true);
}
} catch (InterruptedException e) {
task.cancel(true);
} catch (ExecutionException e) {
task.cancel(true);
} catch (TimeoutException e) {
logger.info("超时");
task.cancel(true);
} logger.info("所有任务执行完毕");
}
}

测试结果

八月 16, 2019 10:05:18 上午 com.thread.MyRunner call
信息: 子线程开始运行
八月 16, 2019 10:05:18 上午 com.thread.Test main
信息: 超时
八月 16, 2019 10:05:18 上午 com.thread.Test main
信息: 所有任务执行完毕

测试结果2 修改 探知时间 task.get(1000,TimeUnit.MILLISECONDS);

八月 16, 2019 10:25:20 上午 com.thread.MyRunner call
信息: 子线程开始运行
八月 16, 2019 10:25:20 上午 com.thread.MyRunner call
信息: 0.5s后,子线程结束运行.100以内的正数和为:5050
八月 16, 2019 10:25:20 上午 com.thread.Test main
信息: 打印submit执行结果?是否done?
八月 16, 2019 10:25:20 上午 com.thread.Test main
信息: true
八月 16, 2019 10:25:20 上午 com.thread.Test main
信息: 所有任务执行完毕
上一篇:Java多线程学习笔记(三)——Future和FutureTask


下一篇:Zookeeper 系列(二)安装配制