java多线程初探(四)取消和关闭

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


猜你喜欢

转载自blog.csdn.net/gcc_java/article/details/79869113