美文网首页
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