Java并发编程实战 - 第7章 取消与关闭

Java没有提供某种抢占式的机制来取消操作或结束线程。

  • 可以通过协作式的中断机制来实现取消操作。
  • 可以使用FutureTask和Executor框架,构建可以取消的任务和服务。

取消任务的原因有:

  • 用户请求取消
  • 有时间限制的操作
  • 应用程序事件
  • 错误
  • 关闭

取消策略

  • How - 其他代码如何(How)请求取消该任务
  • When - 任务在何时检查是否有取消的请求
  • What - 响应取消请求时应该执行哪些操作

取消任务的方式
1. 协作机制 - 设置一个“已请求取消”的标志

public class PrimeGenerator implements Runnable {
    private static ExecutorService exec = Executors.newCachedThreadPool();

    @GuardedBy("this") private final List primes
            = new ArrayList();
    private volatile boolean cancelled;//volatile域来保存一个取消状态,作为取消标识

    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 get() {
        return new ArrayList(primes);
    }

    static List aSecondOfPrimes() throws InterruptedException {
        PrimeGenerator generator = new PrimeGenerator();
        exec.execute(generator);
        try {
            SECONDS.sleep(1);
        } finally {
            generator.cancel();
        }
        return generator.get();
    }
}

2.协作机制 - 中断
如果任务调用了一个阻塞方法,任务可能不会检查取消标识。
很多阻塞方法支持中断,如sleep,wait,join, 它们在响应中断请求的时候执行的操作有:清除中断状态,抛出InterruptedException
所有可以使用中断来取消任务。

public class PrimeProducer extends Thread {
    private final BlockingQueue queue;

    PrimeProducer(BlockingQueue queue) {
        this.queue = queue;
    }

    public void run() {
        try {
            BigInteger p = BigInteger.ONE;
            while (!Thread.currentThread().isInterrupted())
                queue.put(p = p.nextProbablePrime());
        } catch (InterruptedException consumed) {
            /* Allow thread to exit */
        }
    }

    public void cancel() {
        interrupt();
    }
}

如何处理InterruptedException?

  • 传递异常
  • 恢复中断状态

如果不想或无法传递InterruptedException (如Runnable),可以再次调用Interrupt方法来恢复中断状态,让调用栈中的上层代码处理中断请求。
如果代码中没有调用可中断的阻塞方法,也可以通过任务代码检查当前线程中的中断状态来响应中断。

3.在专门的线程中中断任务

public class TimedRun2 {
    private static final ScheduledExecutorService cancelExec = newScheduledThreadPool(1);

    public static void timedRun(final Runnable r, long timeout, TimeUnit unit) throws InterruptedException {
        class RethrowableTask implements Runnable {
            private volatile Throwable t;

            public void run() {
                try {
                    r.run();
                } catch (Throwable t) {
                    this.t = t;
                }
            }

            void rethrow() {
                if (t != null)
                    throw launderThrowable(t);
            }
        }

        RethrowableTask task = new RethrowableTask();
        final Thread taskThread = new Thread(task);
        taskThread.start();
        cancelExec.schedule(new Runnable() {
            public void run() {
                taskThread.interrupt(); 
            }
        }, timeout, unit);

        taskThread.join(unit.toMillis(timeout));

        task.rethrow();
    }
}

4.通过Future来实现取消

public class TimedRun {
    private static final ExecutorService taskExec = Executors.newCachedThreadPool();

    public static void timedRun(Runnable r, long timeout, TimeUnit unit) throws InterruptedException {
        Future task = taskExec.submit(r);
        try {
            task.get(timeout, unit);
        } catch (TimeoutException e) {
            // task will be cancelled below
        } catch (ExecutionException e) {
            // exception thrown in task; rethrow
            throw launderThrowable(e.getCause());
        } finally {
            // Harmless if task already completed
            task.cancel(true); // interrupt if running
        }
    }
}

5.处理不可中断的阻塞
如Socket IO读写,
a) 可以同过override interrupt方法关闭socket来处理不可中断的阻塞。
b) 可以通过override newTaskFor方法,返回一个自定义的可以取消的task来关闭socket。

6.停止基于线程的服务
a)添加取消操作设置取消标识,关闭生产者和消费者服务。
b)使用线程池关闭生产者和消费者服务。
c)使用“毒丸”关闭生产者和消费者服务。
d)通过本地的Executor来处理批量任务(所有任务处理完返回)。

7.记录通过showdownnow方法取消的任务
8.处理非正常的线程终止
9.JVM关闭

你可能感兴趣的:(Java并发编程实战,线程,编程,java,并发)