码迷,mamicode.com
首页 > 编程语言 > 详细

java并发基础(四)--- 取消与关闭

时间:2017-10-04 12:17:15      阅读:262      评论:0      收藏:0      [点我收藏+]

标签:java并发编程   turn   try   volatile   rod   协作式   system   log   阻塞队列   

  《java并发编程实战》的第7章是任务的取消与关闭。我觉得这一章和第6章任务执行同样重要,一个在行为良好的软件和勉强运行的软件之间的最主要的区别就是,行为良好的软件能很完善的处理失败、关闭和取消等过程。

一、任务取消

  在java中没有一种安全的抢占式(收到中断请求就立刻停止)的方式来停止线程,因此也没有安全的抢占式方法来停止任务。只有一些协作式的机制,比如设置请求已取消的标识。在下面的例子中,PrimeGenarator持续的列出素数,直到它被取消。cancel方法将设置cancelled标识,并且主循环在搜索下一个素数之前会检查这个标识,为了使这个过程可靠工作,cancelled必须是volatile类型。

//使用volatile类型的域来保护取消状态
public class PrimeGenerator implements Runnable{
    private final List<BigInteger> primes = new ArrayList<BigInteger>();
    
    private volatile boolean cancelled;
    
    @Override
    public void run() {
        BigInteger p = BigInteger.ONE;
        while (!cancelled) {
             p = p.nextProbablePrime();
             synchronized (this) {
                primes.add(p);
            }
        }
    }
    
    public void cancel(){cancelled = true;}
    
    public synchronized List<BigInteger> get(){
        return new ArrayList<BigInteger>(primes);
    }
}

测试,让genarator只执行一秒。

public static void main(String[] args) throws Exception{
        PrimeGenerator generator = new PrimeGenerator();
        new Thread(generator).start();
        try {
            Thread.sleep(1000);
        }finally{
            generator.cancel();
        }
        
        System.out.println(generator.get());;
}

打印结果就不陈列了,无非就是一些素数而已。

二、中断

  每一个线程都有一个boolean类型的中断状态。当中断线程时,这个线程的中断状态将被设置为true ,在Thread中包含了中断线程以及查询线程中断状态的方法,如下:

public class Thread{
    //中断目标线程(但线程不会立刻停止,也就是不会抢占式停止)
    public void interrupt(){}
    //返回线程的中断状态 已中断:true 未中断:false
    public boolean isInterrupted(){}
    //清除当前线程的中断状态,并返回它之前的值,这是清除中断状态的唯一方法
    public static boolean interrupted(){}
}

  如果一个线程被中断,会发生两件事情:1.清除中断状态 2.抛出InterruptedException异常,所以,有时候如果捕获了InterruptedException后还要有其他操作的话,要把当前线程中断:Thread.currentThread().interrupt();然后再做其他事。

  还有一点需要注意,调用interrupt并不意味着立即停止目标线程正在进行的工作,而只是传递了请求中断的消息。对中断操作的正确理解是:它并不会真正的中断一个正在运行的线程,而只是发出中断请求,然后由线程在下一个合适的时刻自己中断。

  现在我们回过头来看PrimeGenarator的例子,如果代码中出现了阻塞队列,那这种用volatile做标识的取消就可能会有问题,比如基于生产者和消费者模式的素数生成:

//生产者
class BrokenPrimeProducer extends Thread{
    
    private final BlockingQueue<BigInteger> queue;
    private volatile boolean cancelled = false;
    
    public BrokenPrimeProducer(BlockingQueue<BigInteger> queue) {
        this.queue = queue;
    }
    
    @Override
    public void run() {
        try {
            BigInteger p = BigInteger.ONE;
            while (!cancelled) {
                queue.put(p = p.nextProbablePrime());
            }
        } catch (InterruptedException e) {
            
        }
    }
    
    public void cancel(){cancelled = true;}
}

  消费者的代码就不写了,无非是从阻塞队列中取出素数。你看,这个时候,生产者线程生成素数,并将它们放入阻塞队列中,如果生产者的速度超过了消费者的处理速度,队列将被填满,put方法也会被阻塞,如果这时消费者想取消生产者这个任务就无济于事了,因为生产者阻塞在了put方法中。这个问题很好解决,使用中断来替代boolean标识。修改生产者代码:

class PrimeProducer extends Thread{
    
    private final BlockingQueue<BigInteger> queue;
    
    PrimeProducer(BlockingQueue<BigInteger> queue){
        this.queue = queue;
    }
    
    @Override
    public void run() {
        try {
            BigInteger p = BigInteger.ONE;
            //条件改为当前线程是否中断
            while (!Thread.currentThread().isInterrupted()) {
                queue.put(p = p.nextProbablePrime());
            }
        } catch (InterruptedException e) {
            /*允许线程退出*/
        }
    }
    
    public void cancel(){interrupt();}
}

消费者如果不再需要生产者,可以直接中断生产者线程,这样即使生产者处于阻塞状态,一样可以退出。由此可见,中断是实现取消的最合理方式

  

  

 

java并发基础(四)--- 取消与关闭

标签:java并发编程   turn   try   volatile   rod   协作式   system   log   阻塞队列   

原文地址:http://www.cnblogs.com/peterxiao/p/7624931.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!