任务启动很容易,一般会让它们运行直到结束,但有时需要提前结束,或快速关闭,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来实现这样的效果,如图: