java 监听多线程超时:Future

众所周知多线程是个好东西,合理地使用多线程可以提高资源利用率,缩短大批量任务的处理时间。当然高效的同时问题也是少不了的。本文将介绍一些针对线程超时的处理方法,希望对初学者能有所帮助 ^_^


场景

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 处理异步超时



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值