线程的取消与关闭


线程的取消与关闭

在看《java并发编程实战》中的线程的取消和关闭中看到两个例子比较有趣,因此记录下来。

程序目的

想通过某种动作来达到取消某个特定线程的执行,从而达到中断改线程的目的

自定义标志位来控制线程的中断

书中先展示了一种反例,通过自定义标志位来控制线程的中断

  • product
class Producer{
    /**
     * flag标志是否暂停生产
     */
    private volatile Boolean flag = false;

    /**
     * 可阻塞的队列
     */
    private BlockingQueue queue;

    public Producer(BlockingQueue queue){
        this.queue = queue;
    }

    /**
     * 启动生产者
     */
    public void run(){
        try{
            BigInteger p = BigInteger.ZERO;
            while(!flag){
                queue.put(p  = p.nextProbablePrime());
                System.out.println(Thread.currentThread().getName() + ":生产->" + p);
            }
            System.out.println(Thread.currentThread().getName() + ":生产停止");
        }catch (InterruptedException e){
            System.out.println(Thread.currentThread().getName() +":发生中断异常...");
        }
    }

    /**
     * 取消生产
     */
    public void cancel(){
        flag = true;
        System.out.println(Thread.currentThread().getName()+":暂停");
    }
}

通过volatile修饰flag标志位来控制生产者线程的取消

  • main
BlockingQueue<BigInteger> queue = new ArrayBlockingQueue<BigInteger>(3);
Producer producer = new Producer(queue);
new Thread(()->{
    producer.run();
},"生产者线程").start();
Thread.sleep(1000);
producer.cancel();

执行结果:

tSHM5T.png

thread threadId

tSvZfP.png

通过Arthas观察线程的执行情况,生产者线程状态为waiting,可以看到生产者线程在等待阻塞队列释放空间。由于生产者线程会一直等待阻塞队列的释放,所以并不能达到中断生产者线程的效果。

使用中断来进行取消

  • product

class ProducerInterrupt{
    /**
     * 可阻塞的队列
     */
    private BlockingQueue queue;

    public ProducerInterrupt(BlockingQueue queue){
        this.queue = queue;
    }

    /**
     * 启动生产者
     */
    public void run(){
        try{
            BigInteger p = BigInteger.ZERO;
            while(!Thread.currentThread().isInterrupted()){
                queue.put(p  = p.nextProbablePrime());
                System.out.println(Thread.currentThread().getName() + ":生产->" + p);
            }
            System.out.println(Thread.currentThread().getName() + ":生产停止");
        }catch (InterruptedException e){
            System.out.println(Thread.currentThread().getName() +":发生中断异常...");
        }
    }
}
  • main
BlockingQueue<BigInteger> queue = new ArrayBlockingQueue<BigInteger>(3);
ProducerInterrupt producer = new ProducerInterrupt(queue);
Thread t1 =  new Thread(()->{
    producer.run();
},"生产者线程");
t1.start();
Thread.sleep(1000);
t1.interrupt();

执行结果:
tpS00g.png

总结:
在使用自定义的标志位来进行线程的取消操作时,要格外注意是否有会阻塞运行线程的操作,因为阻塞线程后会影响到使用volatile关键字来进行线程操作的判断逻辑。当存在有阻塞运行线程的操作时,会阻塞判断标志位的操作。


  TOC