在中断线程的方法中,除了使用interrupt(),还能否使用别的方法。volatile可以实现变量的可见性,能否用来充当中断标志位?
一、在没有阻塞的时候,可以使用volatile
设计一个实现Runnable的类,在run方法中循环打印出从0到100000中100的倍数。在循环判断的条件中,判断volatile的变量来控制是否停止循环。
在主线程中通过修改其volatile变量,是可以实现使线程停止的。
public class VolatileCanInterrupt implements Runnable {
private volatile boolean canceled = false; //volatile标志位
@Override
public void run() {
int num = 0;
try {
while(num<=100000 && !canceled){
if(num%100==0){
System.out.println(num+"是100的倍数");
}
num++;
Thread.currentThread().sleep(1);
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
VolatileCanInterrupt volatileCanInterrupt = new VolatileCanInterrupt();
Thread t = new Thread(volatileCanInterrupt);
t.start();
Thread.sleep(5000); //等待五秒,然后停止计数线程
volatileCanInterrupt.canceled = true;
}
}
运行结果:
可以看到在2600时线程停止,说明通过修改volatile变量成功地实现了停止线程。
二、在有阻塞的情况下,volatile不再适用
使用经典的生产者消费者问题来表示,通过一个BlockingQueue来存储商品。当满了之后生产者阻塞,等待消费者购买。然后在主线程中通知工厂应该停工了,来终止生产者线程。
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class VolatileError {
public static void main(String[] args) throws InterruptedException {
ArrayBlockingQueue storage = new ArrayBlockingQueue(10);
Producer producer = new Producer(storage);
Thread produceThread = new Thread(producer);
produceThread.start();
Thread.sleep(2000);
Consumer consumer = new Consumer(storage);
while(consumer.needMoreNums()){
System.out.println(consumer.storage.take()+"被消费了");
Thread.sleep(200);
}
System.out.println("消费者不需要商品了,工厂停工");
producer.canceled = true;
}
}
/**
* 生产者线程类
*/
class Producer implements Runnable{
volatile boolean canceled = false;
BlockingQueue storage;
public Producer(BlockingQueue storage) {
this.storage = storage;
}
@Override
public void run() {
int num = 0;
try {
while(num<=100000 && !canceled){
if(num%100==0){
storage.put(num/100);
System.out.println("生产出第"+num/100+"件商品");
}
num++;
Thread.currentThread().sleep(1);
}
}catch (InterruptedException e){
e.printStackTrace();
}finally {
System.out.println("工厂已经停工"); //当停止时打印
}
}
}
class Consumer{
BlockingQueue storage;
public Consumer(BlockingQueue storage) {
this.storage = storage;
}
public boolean needMoreNums(){
if(Math.random()>0.95){
return false;
}
return true;
}
}
运行结果:
可以看到当消费者不需要商品,通知停工后,生产者依然在运行,只是在阻塞,所以无法检测到canceled的变化。
当使用interrupt(),线程会正常中断
使用正统的方法能不能在阻塞时中断线程呢?将上述对volatile变量的操作替换为对中断标志位的操作,使用interrupt()方法。
可以看到,生产者线程很完美的被中断了,所以,只有interrupt()才是正统的方法,它可以在线程阻塞的状态下去中断它。