众所周知多线程是个好东西,合理地使用多线程可以提高资源利用率,缩短大批量任务的处理时间。当然高效的同时问题也是少不了的。本文将介绍一些针对线程超时的处理方法,希望对初学者能有所帮助 ^_^
场景
1. 线程意外地在某个while或者哪里陷入死循环了(如果不是意外地请一定先修正逻辑错误),这个线程就这样一直在死循环中,并且随着时间推移可能会加入新的循环伙伴,最坏的情况是最终所有线程都不干活了。我希望设一个超时时间,将所有超时的线程弄死。
2. 线程执行了某个耗时巨大的任务,而我希望不管执没执行完,都要在限定时间内结束线程。
解决方法
java.util.concurrent.Future
来看下文档里对此接口的介绍
翻译下这段话:
第一句异步计算的结果,听起来像是这样(当然在语法上下面这句是不对的)
Future task = new MyThread().run();
拿到这个result后,我们可以用get()获取线程的执行结果
String result = task.get();
可以用get(time, unit)来获取线程的执行结果,同时监听超时
String result = task.get(1000, TimeUnit.MILLISECONDS);
可以用cancel()来取消任务
task.cancel();
以及其他一些方法
boolean isDone = task.isDone();
boolean isCancelled = task.isCancelled();
开始写代码吧。
监听单线程超时
public class TestThread implements Callable<String> {
@Override
public String call() {
System.out.println(System.currentTimeMillis() + "进来了" + this.hashCode());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {}
return System.currentTimeMillis() + "出来了" + this.hashCode();
}
public static void main(String[] args) {
FutureTask<String> task = new FutureTask<String>(new TestThread());
new Thread(task).start();
try {
//3000就是超时时间
String result = task.get(3000, TimeUnit.MILLISECONDS);
System.out.println(result);
} catch (InterruptedException e) {
System.out.println("线程已经停止了");
} catch (ExecutionException e) {
} catch (TimeoutException e) {
System.out.println(System.currentTimeMillis() + "线程超时了,打死他");
task.cancel(true);
}
}
}
来看看控制台的输出
1524559858691进来了1893883033
1524559861691线程超时了,打死他
是的,很简单就实现了。但其实这里有个大坑,相信你注意到了我在上方写的标题:监听单线程超时-_- 因为get方法是阻塞的。
如果你这样写
FutureTask<String> task = new FutureTask<String>(new TestThread());
FutureTask<String> task2 = new FutureTask<String>(new TestThread());
new Thread(task).start();
new Thread(task2).start();
try {
String result = task.get(3000, TimeUnit.MILLISECONDS);
System.out.println(result);
} catch (InterruptedException | ExecutionException e) {
} catch (TimeoutException e) {
System.out.println(System.currentTimeMillis() + "线程超时了,打死他");
task.cancel(true);
}
try {
String result2 = task2.get(3000, TimeUnit.MILLISECONDS);
System.out.println(result2);
} catch (InterruptedException | ExecutionException e) {
} catch (TimeoutException e) {
System.out.println(System.currentTimeMillis() + "线程超时了,打死他");
task2.cancel(true);
}
看下结果,线程1被打死了,线程2逍遥法外
1524559903444进来了1584403035
1524559903444进来了1893883033
1524559906444线程超时了,打死他
1524559908444出来了1893883033
监听多线程超时
还记得介绍api的段落中那句“以及其他一些方法”吗?
Main类:
public class Main implements Runnable {
//线程池
public static final ExecutorService pool = Executors.newFixedThreadPool(3);
@Override
public void run() {
FutureTask<String> task = new FutureTask<String>(new TestThread());
new Thread(task).start();
long start = System.currentTimeMillis();
while (!task.isDone()) {//关键在这
long now = System.currentTimeMillis();
if ((now - start) >= 3000) {//超时时间3000毫秒
task.cancel(true);
System.out.println(System.currentTimeMillis() + "线程超时了,打死他");
}
try {
Thread.sleep(5);//记得加个sleep,不然搞坏你cpu
} catch (InterruptedException e) {}
}
}
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
pool.execute(new Main());
}
}
}
线程类:
public class TestThread implements Callable<String> {
@Override
public String call() {
System.out.println(System.currentTimeMillis() + "进来了" + this.hashCode());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {}
return System.currentTimeMillis() + "出来了" + this.hashCode();
}
}
来看看Main类的main方法执行结果
1524561058283进来了1966105457
1524561058283进来了227309514
1524561058283进来了120056986
1524561061283线程超时了,打死他
1524561061283线程超时了,打死他
1524561061283线程超时了,打死他
搞定~
其实jdk8有个继承了Future的新类:CompletableFuture,可以实现多线程超时监听。虽然从爹那继承了cancel方法,但是他在cancel后竟然不终止线程!有兴趣的看这篇 用 CompletableFuture 处理异步超时