Java中没有一种安全的抢占式方法来停止线程,只有一种协作式的机制。
大致分为两种协作式机制:1.设置某个”已请求取消“的标志,线程任务定期查看该标志。如果取消标志设置为true,则结束任务
2.调用线程的interrupt()能中断目标线程,通过Thread.currentThread().isInterrupted()方法来查询,也可以通过大多数可阻塞的库函数(如Thread.sleep和Object.wait)来抛出InterruptedException异常,在异常中退出线程。
第一种中断方式的实现代码:
public class PrimeGenerator implements Runnable{
private final List<BigInteger> primes=new ArrayList<BigInteger>();
private volatile boolean cancelled; //需要将cancelled设置为volatile变量,确保当一个线程修改后,对其他线程是可见的
@Override
public void run() {
// TODO Auto-generated method stub
BigInteger p=BigInteger.ONE;
while(!cancelled){
p=p.nextProbablePrime();
synchronized(this){
primes.add(p);
}
}
}
public void cancel(){
cancelled=true;
}
}
这种中断方式在任务调度一个阻塞方法时,例如BlockingQueue.put方法时,会使线程一直阻塞,任务可能永远不会检查取消标志,因此会出现取消失败的情况
第二种中断方式的实现代码:
public class PrimeProducer implements Runnable{
private final BlockingQueue<BigInteger> queue;
PrimeProducer(BlockingQueue<BigInteger> queue){
this.queue=queue;
}
public void run() {
// TODO Auto-generated method stub
try {
BigInteger p = BigInteger.ONE;
while (!Thread.currentThread().isInterrupted()) {
queue.put(p = p.nextProbablePrime());
// Thread.currentThread().sleep(10);
System.out.println(queue.size());
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("中断了,请处理中断");
}
}
public static void main(String[] args){
BlockingQueue<BigInteger> queue=new ArrayBlockingQueue<BigInteger>(999999);
Thread th1=new Thread(new PrimeProducer(queue));
th1.start();
try {
Thread.currentThread().sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
th1.interrupt();
}
}
这种方式使用中断机制才中断,上面代码中有两个位置可以检验出中断:Thread.currentThread().isInterrupted()方法和阻塞方法put的调用。
注意第二种机制的run方法还能写成下面模式,将中断异常处理放在while循环体内,但是必须要在异常处理catch语句块加上Thread.currentThread().interrupt()这句来重置中断状态,否则有可能无法终止任务。这是由于阻塞库方法在检查到线程中断时抛出InterruptException异常前,会清除中断状态。使得while在判断Thread.currentThread().isInterrupted() 时会返回false,则线程不会退出,因此需要在异常处理块重置中断。
public void run() {
// TODO Auto-generated method stub
BigInteger p=BigInteger.ONE;
while(!Thread.currentThread().isInterrupted()){
try {
queue.put(p=p.nextProbablePrime());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("中断了,请处理中断");
Thread.currentThread().interrupt(); ///捕获InterruptedException后恢复中断状态
}
//Thread.currentThread().sleep(10);
System.out.println(queue.size());
}
}
参考资料
《Java Concurrency in Practice》Brian Goetz等著