任务启动很容易,一般会让它们运行直到结束,但有时需要提前结束,或快速关闭,java没有直接提供任何机制来安全终止线程,而是补偿性提供了中断(Interruption),这是一种协作机制。
取消
场景:用户请求取消;有时间限制的操作;应用程序事件(如分解并搜索,其中一个任务搜索得到结果后,其它任务提前结束);错误(例如,爬虫时硬盘装满)
协作机制示例,设置“已请求取消”标志
package gcc.thread.test; import java.math.BigInteger; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.concurrent.TimeUnit; /** * Created by gcc on 2018/4/9. * 一个不断给集合添加质数的操作 */ public class ThreadCancel implements Runnable { //质数集合 private static List<BigInteger> primes = new LinkedList<BigInteger>(); //加volatile,防止指令重排序而导致的cancelled读取偏后,不准确,默认false private volatile boolean cancelled=false; public void run() { BigInteger bigInteger = BigInteger.ONE;//初始化1 while (!cancelled){ bigInteger = bigInteger.nextProbablePrime();//返回一个整数大于该BigInteger的素数 synchronized (this){ primes.add(bigInteger); } } } public void changeCancelled(){ cancelled=true; } public synchronized List<BigInteger> get(){ return new ArrayList<BigInteger>(primes); } List<BigInteger> aSecondOfPrimes() throws InterruptedException{ ThreadCancel threadCancel = new ThreadCancel(); new Thread(threadCancel).start(); try { TimeUnit.SECONDS.sleep(1);//设置休眠时间是一秒,比传统的Thread.sleep(1*1000);写法更优雅,性能基本不受影响 } finally { threadCancel.changeCancelled(); } return threadCancel.get(); } }
package gcc.thread.test; import java.math.BigInteger; import java.util.List; import java.util.concurrent.TimeUnit; /** * Created by gcc on 2018/4/9. * 一个不断给集合添加质数的操作 */ public class ThreadCancelMain { public static void main(String[] args) throws InterruptedException { ThreadCancel threadCancel = new ThreadCancel(); threadCancel.aSecondOfPrimes(); System.out.println(threadCancel.aSecondOfPrimes().size()); } }
示例将一个质数生成器在一秒后取消,通常不会刚好一秒时取消,因为在取消和执行下一次循环之间会有延迟。
可能结果如下:
一个可取消的任务,必须有取消策略,这个策略要详细的定义取消操作的“How”,“When”,“What”,即怎么取消,何时取消,取消操作时执行什么。
问题:取消可能失败,导致永远运行,例如:将上例的primes集合改为BlockingQueue<BigInteger> queue,在while循环里用queue.put(bigInteger)去放对象,如果生产速度大于消费速度,put方法就会堵塞(无法执行),“以请求取消”标志将无法修改而失效。
中断
这是书本截图说明:
因为阻塞而无法取消时,中断成了解决问题的一种途径。
中断并不是立刻中断一个正在运行的线程,而是发出中断请求,在合适的时间中断(这个时间也叫取消点),例如sleep就会严格处理中断请求,会抛出InterruptedException,然后结束sleep()方法。
场景:很多问题永远无法解决(比如枚举所有质素),或者需要过长的时间解决,这样的情况下,指定“在十分钟内枚举质数”,那么会非常有用,这里使用Future来实现这样的效果,如图: