任务取消与关闭

任务取消

Java没有提供任何机制来安全地终止线程。但它提供了中断(Interruption),这是一种协作机制,能够使一个线程终止另一个线程的当前工作。

设置“已请求取消(Cancellation Requested)”标志。

示例代码:

public class PrimeGenerator implements Runnable {
    private volatile boolean cancelled;
    private final List<BigInteger> primes = new ArrayList<>();

    @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<>(primes);
    }

    public static void main(String[] args) throws InterruptedException {
        PrimeGenerator pg = new PrimeGenerator();
        new Thread(pg).start();
        try {
            SECONDS.sleep(1);
        } finally {
            pg.cancel();
        }
        List res = pg.get();
        System.out.println("Size: " + res.size() + " " + res);
    }
}

通常,中断是实现取消的最合理方式。Thread中的中断方法如下:

public class Thread {
    /**
     * 中断目标线程,即自己
     **/
	public void interrupt() {...}
	/**
     * 返回目标线程的中断状态
     **/
	public boolean isInterrupted() {...}
	/**
     * 清除当前线程的中断状态,并返回它之前的值
    **/
	public static boolean interrupted() {...}
}

示例代码:

public class NewPrimeGenerator extends Thread {
    private final BlockingQueue<BigInteger> queue = new ArrayBlockingQueue<BigInteger>(1000000);

    @Override
    public void run() {
        try {
            BigInteger p = BigInteger.ONE;
            queue.put(p);
            while (!Thread.currentThread().isInterrupted()) {
                p = p.nextProbablePrime();
                queue.put(p);
            }
        } catch (InterruptedException e) {
            System.out.println("Interrupted exit....");
        } catch (Exception a) {
            System.out.println("Other Exception" + a);
        }
    }

    public void cancel() {
        interrupt();
    }

    public List<BigInteger> get() {
        return new ArrayList<>(queue);
    }


    public static void main(String[] args) throws InterruptedException {
        NewPrimeGenerator npg = new NewPrimeGenerator();
        npg.start();
        try {
            SECONDS.sleep(1);
        } finally {
            npg.cancel();
        }
        List res = npg.get();
        System.out.println("Size: " + res.size() + " " + res);
    }
}

通过Future来实现取消

Future拥有一个cancel方法,该方法带有一个boolean类型的参数mayInterruptIfRunning,表示取消操作是否可以成功。

示例代码:

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) {
        //任务将被取消
    } catch (ExecutionException e) {
        throw launderThrowable(e.getCause());
    } finally {
        task.cancel(true);//如果任务正在运行,则将被中断,否则直接取消
    }
}

封装非标准的取消操作

如果一个线程由于执行同步的Socket I/O或者等待获得内置锁而阻塞,那么中断请求只能设置线程的中断状态,除此之外没有其他任何作用。

示例代码:

public class ReaderThread extends Thread {
    private final Socket socket;
    private final InputStream in;
    private final int BUFSIZ = 1024;

    private ReaderThread(Socket socket) throws IOException {
        this.socket = socket;
        this.in = socket.getInputStream();
    }

    @Override
    public void interrupt() {
        try {
            socket.close();
        } catch (IOException e) {
        } finally {
            super.interrupt();
        }
    }

    @Override
    public void run() {
        try {
            byte[] buf = new byte[BUFSIZ];
            while (true) {
                int count = in.read(buf);
                if (count < 0)
                    break;
                else if (count > 0)
                    processBuffer(buf, count);
            }
        } catch (IOException e) {
        }
    }

    private void processBuffer(byte[] buf, int count) {
        //some work done
    }
}

任务关闭

通过解决竞态条件问题提供可靠关闭操作。

代码:

public class LogService {
    private final BlockingQueue<String> queue = new ArrayBlockingQueue<String>(Integer.MAX_VALUE);
    private final LoggerThread loggerThread = new LoggerThread();
    private final PrintWriter writer;
    private boolean isShutdown = false;
    private int reservations = 0;

    public LogService(PrintWriter writer) { this.writer = writer; }

    public void start() { loggerThread.start(); }
    
    public void stop() {
        synchronized (this) { isShutdown = true; }
        loggerThread.interrupt();
    }

    public void log(String msg) throws InterruptedException {
        synchronized (this) {
            if (isShutdown)
                throw new IllegalStateException("Service shutdown!");
            ++reservations;
        }
        queue.put(msg);
    }
    
    private class LoggerThread extends Thread {
        public void run() {
            try {
                while (true) {
                    try {
                        synchronized (LogService.this) {
                            if (isShutdown && reservations == 0)
                                break;
                        }
                        String msg = queue.take();
                        synchronized (LogService.this) {
                            --reservations;
                        }
                        writer.println(msg);
                    } catch (InterruptedException e) {
                        // retry?
                    }
                }
            } finally {
                writer.close();
            }
        }
    }
}

使用shutdown以及shutdownNow方法,关闭ExecutorService

示例代码:

public class NewLogService {
    private final ExecutorService exec = Executors.newSingleThreadExecutor();
    private PrintWriter writer;

    public NewLogService(PrintWriter writer) {
        this.writer = writer;
    }

    public void start() {
    }

    public void stop() throws InterruptedException {
        try {
            exec.shutdown();
            exec.awaitTermination(10, TimeUnit.SECONDS);
        } finally {
            writer.close();
        }
    }
    public void log(String msg) {
        try {
            exec.execute(new WriteTask(msg));
        } catch (RejectedExecutionException e) {
        }
    }
}

“毒丸”对象

生产者再提交了“毒丸”对象后,将不会提交其他工作,消费者接收到“毒丸”对象之前完成队列中的所有工作

示例代码:

public class IndexingService {
    private static final File POISON = new File("");
    private final IndexerThread consumer = new IndexerThread();
    private final CrawlerThread producer = new CrawlerThread();
    private final BlockingQueue<File> queue = new ArrayBlockingQueue<File>(Integer.MAX_VALUE);
    private final FileFilter fileFilter;
    private final File root;

    public IndexingService(FileFilter fileFilter, File root) {
        this.fileFilter = fileFilter;
        this.root = root;
    }

    class CrawlerThread extends Thread {
        @Override
        public void run() {
            try {
                crawl(root);
            } catch (Exception e) {
            } finally {
                while (true) {
                    try {
                        queue.put(POISON);
                        break;
                    } catch (InterruptedException e) {
                        //retry
                    }
                }
            }
        }

        private void crawl(File root) { /* dig */ }

    }

    class IndexerThread extends Thread {
        @Override
        public void run() {
            try {
                while (true) {
                    File file = queue.take();
                    if (POISON == file) {
                        break;
                    } else {
                        indexFile(file);
                    }
                }
            } catch (Exception e) {
            } finally {
                while (true) {
                    try {
                        queue.put(POISON);
                        break;
                    } catch (InterruptedException e) {
                        //retry
                    }
                }
            }
        }

        private void indexFile(File file) { /* index */ }

    }
}

未捕获异常的处理

在Thread API中提供了UncaughtExceptionHandler,它能检测出某个线程由于未捕获的异常而终结的情况。只有通过execute提交的任务,才能将他抛出的异常交给未捕获异常处理器,而通过submit提交的任务,无论是抛出的未检查异常还是已检查异常,都将被认为是任务返回状态的一部分。

示例代码:

public class UEHTest {
    private static class MyUEH implements Thread.UncaughtExceptionHandler {
        @Override
        public void uncaughtException(Thread t, Throwable e) {
            Logger logger = Logger.getAnonymousLogger();
            logger.log(Level.SEVERE, "Thread terminated with exception: " + t.getName(), e);
        }
    }

    private static class MyThread extends Thread {
        @Override
        public void run() {
            Integer.parseInt("aaa");
        }
    }

    public static void main(String[] args) {
        Thread myThread = new MyThread();
        myThread.setUncaughtExceptionHandler(new MyUEH());
        myThread.start();
        //Not working
        /*ExecutorService exec = Executors.newCachedThreadPool();
        exec.execute(myThread);*/
}

}

示例代码:

public class UEHExecutorService {

    private static class MyUEH implements Thread.UncaughtExceptionHandler {
        @Override
        public void uncaughtException(Thread t, Throwable e) {
            Logger logger = Logger.getAnonymousLogger();
            logger.log(Level.SEVERE, "Thread terminated with exception: " + t.getName(), e);
        }
    }

    private static class MyThreadFactory implements ThreadFactory {
        private final Thread.UncaughtExceptionHandler handler = new MyUEH();

        @Override
        public Thread newThread(Runnable r) {
            Thread thread = new Thread(r);
            thread.setUncaughtExceptionHandler(handler);
            return thread;
        }
    }

    public static void main(String[] args) {
        ThreadFactory factory = new MyThreadFactory();
        ExecutorService exec = new ThreadPoolExecutor(10, 100, 180, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), factory);
        Thread thread = factory.newThread(new Runnable() {
            @Override
            public void run() {
                Integer.parseInt("abc");
            }
        });
        exec.execute(thread);
    }
}

参考:《Java并发编程实战》、为线程池中的每个线程设置UncaughtExceptionHandler线程池执行UncaughtExceptionHandler失效问题分析

发布了11 篇原创文章 · 获赞 1 · 访问量 1635

猜你喜欢

转载自blog.csdn.net/u011578173/article/details/95012280