众所周知多线程是个好东西,合理地使用多线程可以提高资源利用率,缩短大批量任务的处理时间。当然高效的同时问题也是少不了的。本文将介绍一些针对线程超时的处理方法,希望对初学者能有所帮助 ^_^
场景
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 result = task2.get(3000, TimeUnit.MILLISECONDS); System.out.println(result); } catch (InterruptedException | ExecutionException e) { } catch (TimeoutException e) { System.out.println(System.currentTimeMillis() + "线程超时了,打死他"); task.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() + "线程超时了,打死他"); } } } public static void main(String[] args) { for (int i = 0; i < 3; i++) { pool.execute(new Main()); } } }
线程类:
public class TestThread implements Callable<String> { public static final ExecutorService pool = Executors.newFixedThreadPool(5); @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线程超时了,打死他搞定~