线程优雅关闭

1.暴力停止

从外部直接调用该线程的 stop 方法,直接把线程停下来。会造成数据不一致。不推荐,并且方法已经过时。

2.使用变量通知

通过volatile关键字定义一个变量,通过判断变量来停止线程。能适应大部分情况。

缺点:这个方法有一个潜在的大漏洞,就是若线程进入了阻塞状态,我们将不能通过修改volatile变量来停止线程

public class Tasks implements Runnable {
    //增加关闭标记,注意内存可见性
    private static volatile boolean stop = false;

    @Override
    public void run() {
        while (!stop) {
            System.out.println("执行线程中");
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    //测试
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new Tasks());
        thread.start();

        Thread.sleep(1000);
        stop = true;
    }
}

3.使用中断(推荐)

通过 java 内定的机制响应中断:当线程调用 sleep(),wait() 方法后进入阻塞后,如果线程在阻塞的过程中被中断了,那么线程会捕获或抛出一个中断异常,我们可以根据这个中断异常去控制线程的停止。

这个可以避免堵塞无法关闭的情况。

/**
 * 通过生产者消费者模式演示volatile的局限性,volatile不能唤醒已经阻塞的线程
 * 生产者生产速度很快,消费者消费速度很慢,通过阻塞队列存储商品
 */
public class Demo8 {
    public static void main(String[] args) throws InterruptedException {
        ArrayBlockingQueue storage = new ArrayBlockingQueue(10);

        Producer producer = new Producer(storage);
        Thread producerThread = new Thread(producer);
        producerThread.start();
        Thread.sleep(1000);//1s足够让生产者把阻塞队列塞满

        Consumer consumer = new Consumer(storage);
        while (consumer.needMoreNums()) {
            System.out.println(storage.take() + "被消费");
            Thread.sleep(100);//让消费者消费慢一点,给生产者生产的时间
        }

        System.out.println("消费者消费完毕");
        // producer.canceled = true;//让生产者停止生产(实际情况是不行的,因为此时生产者处于阻塞状态,volatile不能唤醒阻塞状态的线程)
        producerThread.interrupt();
    }
}

class Producer implements Runnable {

    public volatile boolean canceled = false;

    private BlockingQueue storage;

    public Producer(BlockingQueue storage) {
        this.storage = storage;
    }

    @Override
    public void run() {
        int num = 0;
        try {
            while (num < Integer.MAX_VALUE / 2 && !Thread.currentThread().isInterrupted()) {
                if (num % 100 == 0) {
                    this.storage.put(num);
                    System.out.println(num + "是100的倍数,已经被放入仓库");
                }
                num++;
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println("生产者停止生产");
        }
    }
}

class Consumer {
    private BlockingQueue storage;

    public Consumer(BlockingQueue storage) {
        this.storage = storage;
    }

    public boolean needMoreNums() {
        return Math.random() < 0.95 ? true : false;
    }

上次更新时间: 2024/5/7 05:59:02