如何使用ExecutorService和Future优雅地退出

时间:2013-09-12 12:34:35

标签: java concurrency future

我有三个线程Producer,Processor,Consumer,并且都有阻塞队列来共享它们之间的数据。我想加入这些线程,我正在使用未来,所以代码看起来像 -

public class Test {


    private static class Producer implements Runnable {

        private final BlockingQueue<Integer> queue;

        private Producer(BlockingQueue<Integer> queue) {
            this.queue = checkNotNull(queue);
        }

        @Override public void run() {
            try {
                int i = 0;
                while (++i < Integer.MAX_VALUE) {
                    addEntry(i);
                }
            } finally {
                addEntry(-1);
            }
        }

        private void addEntry(int i) {
            try {
                queue.put(i);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }

    private static class Processor implements Runnable {

        private final BlockingQueue<Integer> readQueue;
        private final BlockingQueue<Integer> writeQueue;

        private Processor(BlockingQueue<Integer> readQueue, BlockingQueue<Integer> writeQueue) {
            this.readQueue = checkNotNull(readQueue);
            this.writeQueue = checkNotNull(writeQueue);
        }

        @Override public void run() {
            try {
                int i = readQueue.take();
                while (i != -1) {
                    writeQueue.put(i);
                    i = readQueue.take();
                    if(i==1000){
                        throw new NullPointerException();
                    }
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            } finally {
                addEntry(-1);
            }
        }

        private void addEntry(int i) {
            try {
                writeQueue.put(i);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

    private static class Consumer implements Runnable {

        private final BlockingQueue<Integer> queue;

        private Consumer(BlockingQueue<Integer> queue) {
            this.queue = checkNotNull(queue);
        }

        @Override public void run() {
            try {
                int i = queue.take();
                while (i != -1) {
                    System.out.println(i);
                    i = queue.take();
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }

        }
    }

    public static void main(String[] args) {
        BlockingQueue<Integer> readQueue = new ArrayBlockingQueue<>(1000);
        BlockingQueue<Integer> writeQueue = new ArrayBlockingQueue<>(1000);
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        Runnable[] runnables = new Runnable[]{new Producer(readQueue), new Processor(readQueue, writeQueue), new Consumer(writeQueue)};
        List<Future<?>> futures = Lists.newArrayList();
        for (Runnable runnable : runnables) {
            futures.add(executorService.submit(runnable));
        }
        executorService.shutdown();
        for (Future<?> future : futures) {
            try {
                future.get();
            } catch (InterruptedException e) {
                executorService.shutdownNow();
                Thread.currentThread().interrupt();
            } catch( ExecutionException e){
                executorService.shutdownNow();
                throw new RuntimeException(e);
            }finally{
                future.cancel(true);
            }
        }
        System.out.println("Done..");
    }
}

现在,如果Futute#get()抛出异常(处理器中的NPE),我想停止所有线程(Producer,Processor,Consumer)并正常退出。

我怎么能做到这一点?

1 个答案:

答案 0 :(得分:0)

您无法强制线程退出。您只能发出信号并将线程编码为接收信号时退出。您可以使用线程中断作为信号线程退出的方式。如果要在调用executorService.shutdownNow()时执行线程退出,则线程应在中断时退出。

例如,在Consumer

    @Override public void run() {
        try {
            int i = queue.take();
            while (i != -1) {
                System.out.println(i);
                i = queue.take();
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }

当线程被中断时,它只会再次调用interrupt()。这没有任何成就。 run方法收到InterruptedException时会返回:

    @Override public void run() {
        try {
            int i = queue.take();
            while (i != -1) {
                System.out.println(i);
                i = queue.take();
            }
        } catch (InterruptedException e) {
            System.err.println("Consumer interrupted");
            return;
        }