美文网首页
Java错误的停止线程的方法

Java错误的停止线程的方法

作者: _灯火阑珊处 | 来源:发表于2020-06-17 13:50 被阅读0次

1. 被弃用的 stop、suspend 和 resume 方法

stop() 来停止线程,会导致线程运行一半突然停止,没办法完成一个基本单位的操作,会造成脏数据;
模拟连队发装备代码示例:

public class StopThread implements Runnable {
    @Override
    public void run() {
        for (int i = 1; i <= 5; i++) {
            System.out.println("连队" + i + " 开始领取:");
            for (int j = 1; j <= 10; j++) {
                System.out.println("士兵" + j + " 开始领取");
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("====连队" + i + "领取完毕====");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new StopThread());
        thread.start();
        // 等待666毫秒,停止线程
        Thread.sleep(666);
        thread.stop();
    }
}

运行结果:

连队1 开始领取:
士兵1 开始领取
士兵2 开始领取
士兵3 开始领取
士兵4 开始领取
士兵5 开始领取
士兵6 开始领取
士兵7 开始领取
士兵8 开始领取
士兵9 开始领取
士兵10 开始领取
====连队1领取完毕====
连队2 开始领取:
士兵1 开始领取
士兵2 开始领取
士兵3 开始领取

Process finished with exit code 0

会发现 连队2 才3个人领完,其他人都还没有领到,线程突然就结束了,这样就会造成数据的错乱!
并且这种数据的错乱后期难以排查!
Oracle官方文档对于为什么Thread.stop不推荐使用的解释
https://docs.oracle.com/javase/7/docs/technotes/guides/concurrency/threadPrimitiveDeprecation.html

image.png

suspend和resume被抛弃原因:
suspend和stop不一样,suspend不会破坏对象,但是它会让一个线程挂起,在恢复之前,锁不会释放,也就是它是带着锁去进行休息的,这样的话很容易造成死锁。
resume

2. 用volatile设置boolean标记位

2.1:看似可行的代码

public class WrongWayVolatile implements Runnable {

    /**
     * 设置boolean类型的标记位
     */
    private volatile boolean canceled = false;

    public static void main(String[] args) throws InterruptedException {
        WrongWayVolatile r = new WrongWayVolatile();
        Thread t = new Thread(r);
        t.start();

        // 等待5秒后
        Thread.sleep(5000);
        // 更改canceled值以达到停止程序的目的
        r.canceled = true;
    }

    @Override
    public void run() {
        int num = 0;
        try {
            while (num <= Integer.MAX_VALUE && !canceled) {
                if (num % 100 == 0) {
                    System.out.println(num + "是100的倍数");
                }
                num++;
                Thread.sleep(1);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

程序在运行5秒钟后停止了,达到了预期的效果。

2.2:当陷入阻塞时,无法停止
下面代码示例中,生产者的生产速度很快,消费者消费速度慢,
所以阻塞队列满了以后,生产者会阻塞停止生产,等待消费者进一步消费

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public class WrongWayVolatileCantStop {

    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();
        // 等待1秒让生产者将队列填满
        Thread.sleep(1000);

        Consumer consumer = new Consumer(storage);
        while (consumer.needMoreNums()) {
            System.out.println(consumer.storage.take() + "被消费了!");
            // 消费是需要时间的,设置个100毫秒
            Thread.sleep(100);
        }
        System.out.println("消费者不需要更多数据了。");

        // 一旦消费者不需要更多数据了,则应该让生产者停下来,
        // 将标记位设置为true,看是否能将线程停止?
        producer.canceled = true;
        System.out.println(producer.canceled);
    }

}

/**
 * 生产者
 */
class Producer implements Runnable {

    /**
     * 设置boolean类型的标记位
     */
    public volatile boolean canceled = false;

    BlockingQueue storage;

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

    @Override
    public void run() {
        int num = 0;
        try {
            while (num <= Integer.MAX_VALUE && !canceled) {
                if (num % 100 == 0) {
                    storage.put(num);
                    System.out.println(num + "是100的倍数,被放到了仓库中。");
                }
                num++;
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println("生产者结束运行");
        }
    }
}

/**
 * 消费者
 */
class Consumer {
    BlockingQueue storage;

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

    public boolean needMoreNums() {
        // 随机返回true或false
        if (Math.random() > 0.95) {
            return false;
        }
        return true;
    }
}

最后的运行结果并没有打印出 “生产者结束运行”,
而且程序也没有停止!!!

程序没有停止
为什么?
因为生产者暂停生产时,是阻塞在 storage.put(num);
而且也没有人去唤醒,所以 while() 条件也无法执行,也不知道 canceled 的值已经被改变!
只会一直等在 storage.put(num); 这里。
解决方法:使用 interrupt
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public class WrongWayVolatileCantStop {

    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();
        // 等待1秒让生产者将队列填满
        Thread.sleep(1000);

        Consumer consumer = new Consumer(storage);
        while (consumer.needMoreNums()) {
            System.out.println(consumer.storage.take() + "被消费了!");
            // 消费是需要时间的,设置个100毫秒
            Thread.sleep(100);
        }
        System.out.println("消费者不需要更多数据了。");

        // 一旦消费者不需要更多数据了,则应该让生产者停下来,
        // 使用interrupt通知停止线程
        producerThread.interrupt();
    }

}

/**
 * 生产者
 */
class Producer implements Runnable {
    BlockingQueue storage;

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

    @Override
    public void run() {
        int num = 0;
        try {
            while (num <= Integer.MAX_VALUE && !Thread.currentThread().isInterrupted()) {
                if (num % 100 == 0) {
                    storage.put(num);
                    System.out.println(num + "是100的倍数,被放到了仓库中。");
                }
                num++;
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println("生产者结束运行");
        }
    }
}

/**
 * 消费者
 */
class Consumer {
    BlockingQueue storage;

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

    public boolean needMoreNums() {
        // 随机返回true或false
        if (Math.random() > 0.95) {
            return false;
        }
        return true;
    }
}

相关文章

网友评论

      本文标题:Java错误的停止线程的方法

      本文链接:https://www.haomeiwen.com/subject/haehxktx.html