商务服务
暂停线程(如何优雅地停止线程)
2023-05-08 07:27

暂停线程(如何优雅地停止线程)

使用 cancelled 标志,当标志状态为true的时候,停止线程。

示例:

public class Test0701CancelThread implements Runnable {    public static void main(String[] args) {
        Test0701CancelThread cancelThread = new Test0701CancelThread();        try {
            cancelThread.aSecondOfPrimes();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }    private final List<BigInteger> primes = new ArrayList<>();    private volatile boolean cancelled;    @Override
    public void run() {
        System.out.println("执行线程");
        BigInteger p = BigInteger.ONE;        while (!cancelled) {
            p = p.nextProbablePrime();            synchronized (this) {
                primes.add(p);
            }
        }
    }    public void cancel() {
        System.out.println("取消线程");
        cancelled = true;
    }    public synchronized List<BigInteger> get() {        return new ArrayList<>(primes);
    }    
    public List<BigInteger> aSecondOfPrimes() throws InterruptedException {
        Test0701CancelThread cancelThread = new Test0701CancelThread();        new Thread(cancelThread).start();        try {
            SECONDS.sleep(1);
        } finally {
            cancelThread.cancel();
        }        return cancelThread.get();
    }
}

问题:

1、如果调用阻塞方法,可能会导致永远也不会检查取消标志,导致线程不能被终止。

通过 Thread 类的中断方法 interrupt() 方法,中断线程。这是停止线程的最佳实践,它不会马上停止线程,而会先保留必要的内容,然后安全地停止线程。

示例:

public class Test0705CancelThread extends Thread {

    private final BlockingQueue<BigInteger> queue;    public Test0705CancelThread(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) {
            e.printStackTrace();
        }
    }    public void cancel() {
        interrupt();
    }
}

注意:

Future 类,提供的 cancel() 方法,可以停止 Future 任务。

示例:

public class Test0710FutureCancelThread {

    private static final ScheduledExecutorService cancelExec = Executors.newScheduledThreadPool(5);    public static void timeRun(final Runnable r, long timeout, TimeUnit unit) {
        Future<?> task = cancelExec.submit(r);        try {
            task.get();
        } catch (InterruptedException e) {            // 取消线程
            task.cancel(true);
        } catch (ExecutionException e) {            // 如果任务已经执行完毕,那么执行取消不会有什么影响
            // 如果任务正在执行,那么将被中断
            task.cancel(true);
        }
    }
}

也是取消线程的正确姿势:通过Future来取消线程。当Future.get抛出 InterruptedException或者TimeoutException时,如果你知道不再需要结果,那么就可以调用Future.cancel来取消任务

线程池的停止,可以调用 ExecutorService 类提供的 shutdown() 方法进行停止。

示例:

public class Test0716CloseExecutorService {    private static final long TIMEOUT = 1000L;    private static final TimeUnit UNIT = TimeUnit.MILLISECONDS;    private final ExecutorService exec = Executors.newScheduledThreadPool(5);    private final PrintWriter writer;    public Test0716CloseExecutorService(PrintWriter writer) {        this.writer = writer;
    }    public void start() {
    }    public void stop() throws InterruptedException {        try {
            exec.shutdown();            // TODO 问:为什么停止线程,需要调用以下语句
            // 这个方法就是调用shutdown() 之后等待任务执行完毕的方法,可以查看源码的注释
            // Blocks until all tasks have completed execution after a shutdown
            // request, or the timeout occurs, or the current thread is
            // interrupted, whichever happens first.
            exec.awaitTermination(TIMEOUT, UNIT);
        } finally {
            writer.close();
        }
    }    public void log(String msg) {        try {
            exec.execute(new WriteTask(msg));
        } catch (RejectedExecutionException ignored) {

        }
    }
}

核心语句如下:

// 不接收新任务exec.shutdown();// 停止之前提交的任务exec.awaitTermination(TIMEOUT, UNIT);

在生产者-消费者模式中,通过在队列中增加毒丸对象类停止线程。

示例:

public class Test0717PoisonPillCancelThread {    private static final File POSION = new File("");    private final IndexerThread consumer = new IndexerThread();    private final CrawlerThread producer = new CrawlerThread();    private final BlockingQueue<File> queue;    private final FileFilter fileFilter;    private final File root;    public Test0717PoisonPillCancelThread(BlockingQueue<File> queue, FileFilter fileFilter, File root) {        this.queue = queue;        this.fileFilter = fileFilter;        this.root = root;
    }    public void start() {
        producer.start();
        consumer.start();
    }    public void stop() {
        producer.interrupt();
    }    public void awaitTermination() throws InterruptedException {
        consumer.join();
    }    
    public class IndexerThread extends Thread {        @Override
        public void run() {            try {                while (true) {
                    File file = queue.take();                    // 毒丸对象 停止消费者
                    if (file == POSION) {                        break;
                    } else {
                        indexFile(file);
                    }
                }
            } catch (InterruptedException e) {                // deal with exception
            } finally {

            }
        }        private void indexFile(File file) {
        }        private void crawl(File root) throws InterruptedException {

        }
    }    
    public class CrawlerThread extends Thread {        @Override
        public void run() {            try {
                crawl(root);
            } catch (InterruptedException e) {                // deal with exception
            } finally {                while (true) {                    try {                        // 队列中存入毒丸
                        queue.put(POSION);                        break;
                    } catch (InterruptedException e1) {                        // deal with exception
                    }
                }
            }
        }        private void crawl(File root) throws InterruptedException {

        }
    }
}

毒丸对象是指放在队列上的对象,其含义是:当得到这个对象时,立即停止。

只有在生产者和消费者的数量都已知的情况下,才可以使用毒丸对象。有多少个生产者,就需要多少个毒丸对象,这样才能停止所有的生产者服务。


Tencent Kona JDK11无暂停内存管理ZGC生产实践

    以上就是本篇文章【暂停线程(如何优雅地停止线程)】的全部内容了,欢迎阅览 ! 文章地址:http://www.uqian.cn/news/2879.html 
     资讯      企业新闻      行情      企业黄页      同类资讯      首页      网站地图      返回首页 极顶速云移动站 http://m.uqian.cn/ , 查看更多   

点击拨打: