• 线程中断


    1.stop()方法

    中断Java线程,首当其冲的当然是“方便的”stop()方法啦!直接调用,线程就会中止运行。 但是这个方法却在API中被标明为废弃,这是为什么? 这是正是因为它的“方便”导致的,想象某一个数据处理线程,数据处理到一半, 然后调用stop()方法,线程自然会被中断,但是他还有一半的数据没有处理! 这就破坏了数据的一致性,所以被废弃了。

    public class Test {
        public static void main(String[] args) {
    
            User user = new User();
            user.setID(1);
            user.setName(String.valueOf(1));
            Thread readThread = new Thread(new ReadTask(user));
            readThread.start();
            try {
                while(true) {
                    Thread writeThread = new Thread(new WriteTask(user));
                    writeThread.start();
                    Thread.sleep(150);
                    writeThread.stop(); //a deprecated method
                }
            } catch (InterruptedException e) {
                throw new RuntimeException("the "+Thread.currentThread().getName()+" has been interrupted",e);
            }    
        }
    
        // ---------------
        // 内部类
        // ---------------
        private static class User {
    
            private long ID;
            private String name;
    
            public User() {
    
            }
    
            public long getID() {
                return ID;
            }
    
            public void setID(long iD) {
                ID = iD;
            }
    
            public String getName() {
                return name;
            }
    
            public void setName(String name) {
                this.name = name;
            }
    
            @Override
            public String toString() {
                return "User [ID=" + ID + ", name=" + name + "]";
            }
        }
    
        private static class WriteTask implements Runnable {
    
            private User user;
    
            public WriteTask(User user) {
                this.user = user;
            }
    
            @Override
            public void run() {
    
                while(true) {
                    long currentTimeMillis = System.currentTimeMillis();
                    user.setID(currentTimeMillis);
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        throw new RuntimeException("the "+ThreadUtil.currentThreadName()+" has been interrupted");
                    }
                    user.setName(String.valueOf(currentTimeMillis));
                }
            }
    
        }
    
        private static class ReadTask implements Runnable {
    
            private User user;
    
            public ReadTask(User user) {
                this.user = user;
            }
    
            @Override
            public void run() {
    
                while(true) {
                    if(user.getID()!=Long.parseLong(user.getName())) { //一旦出现数据不一致的情况就输出
                        System.out.println(user);
                    }
                    Thread.yield();
                }
            }
        }
    }
    /*****************************
    控制台打印如下
    ...
    User [ID=1489157102274, name=1489157102118]
    User [ID=1489157102274, name=1489157102118]
    User [ID=1489157102274, name=1489157102118]
    User [ID=1489157102274, name=1489157102118]
    User [ID=1489157102274, name=1489157102118]
    ...
    ******************************/

    显然,上面这段代码,会在控制台打印大段信息。 WriteTask先setID,然后阻塞自身,如果此时调用了stop, 那么setName就不会调用,数据处理到一半被中断了,从而破坏数据的一致性。 当然,更可怕的是它的静默,这种错误没有任何异常信息,一旦出现问题很难定位,严重降低调试效率。 所以还是尽量不用stop()方法

    2.中断位

    利用stop()中断线程,其不会通知任何人说:“我要中断了!”,而是自顾自的中断自身。 这样,我们没法做善后处理。理想情况下,如果线程中断了, 要是能够通知我们:“线程中断了,快做一些善后处理!”,就好了。 这时,我们引入一个中断标志位,通过它来判断线程是否中断并做相应的处理。

    public class Test {
        public static void main(String[] args) {
    
            User user = new User();
            user.setID(1);
            user.setName(String.valueOf(1));
            Thread readThread = new Thread(new ReadTask(user));
            readThread.start();
            WriteTask writeTask = new WriteTask(user);
            try {
                while(true) { //开启多个线程
                    Thread writeThread = new Thread(writeTask);
                    writeThread.start();
                    Thread.sleep(150);
                    writeTask.interrupt();
                }
            } catch (InterruptedException e) {
                throw new RuntimeException("the "+Thread.currentThread().getName()+" has been interrupted",e);
            }    
        }
        //---------------
        // 内部类
        //---------------
        private static class User {
    
            private long ID;
            private String name;
            public User() {
    
            }
            public long getID() {
                return ID;
            }
            public void setID(long iD) {
                ID = iD;
            }
            public String getName() {
                return name;
            }
            public void setName(String name) {
                this.name = name;
            }
            @Override
            public String toString() {
                return "User [ID=" + ID + ", name=" + name + "]";
            }
        }
    
        private static class WriteTask implements Runnable {
    
            private boolean interrupt;
            private User user;
            public WriteTask(User user) {
                this.user = user;
            }
            @Override
            public void run() {
    
                while(true) {
                    synchronized(user) {
                        if(interrupt) {
                            System.out.println("the "+Thread.currentThread().getName()+" has been interrupted");
                            break;
                        }
                        long currentTimeMillis = System.currentTimeMillis();
                        user.setID(currentTimeMillis);
                        try {
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            throw new RuntimeException("the "+Thread.currentThread().getName()+" has been interrupted",e);
                        }
                        user.setName(String.valueOf(currentTimeMillis));
                    }
                    Thread.yield();
                }
            }
            public void interrupt() {
                this.interrupt = true;
            }
        }
    
        private static class ReadTask implements Runnable {
    
            private User user;
            public ReadTask(User user) {
                this.user = user;
            }
            @Override
            public void run() {
    
                while(true) {
                    synchronized(user) {
                        if(user.getID()!=Long.parseLong(user.getName())) { //一旦出现数据不一致的情况就输出
                            System.out.println(user);
                        }
                    }
                    Thread.yield();
                }
            }
        }
    }
    /*************************
    控制台打印如下
    ...
    the Thread-2 has been interrupted
    the Thread-1 has been interrupted
    the Thread-3 has been interrupted
    the Thread-4 has been interrupted
    ...
    ***************************/

    上面这段代码,ReadTask不会打印任何信息了,因为数据的一致性得到保证。 利用设置中断标志位interrupt,“温和的”告知线程:“要中断了,但怎么处理随你”。

    3.interrupt()方法

    interrupt()方法就是利用上述中断标志位的方式实现线程中断的。 他会调用一个native方法,设置线程的中断标志位。 另外,判断线程是否中断还有两个方法,静态方法interrupted(),实例方法isInterrupted()。 看下源码,JDK1.8的

      /**
         * 一个静态方法,清除当前中断标志
         */
        public static boolean interrupted() {
            return currentThread().isInterrupted(true);
        }
    
        /**
         * 实例方法,不会清除当前中断标志
         */
        public boolean isInterrupted() {
            return isInterrupted(false);
        }
    
        /**
         * 一个本地方法,返回线程中断情况
         * 而且当ClearInterrupted为true时,
         * 调用isInterrupted会清除当前线程的中断标志
         */
        private native boolean isInterrupted(boolean ClearInterrupted);

    我们试试isInterrupted()是如何工作的

    public class Test {
        public static void main(String[] args) {
            Thread t = new Thread(new Task());
            t.start();
            t.interrupt(); //设置中断标志位
        }
        private static class Task implements Runnable{
            @Override
            public void run() {
                System.out.println(Thread.isInterrupted()); //返回当前中断情况,true;
                System.out.println(Thread.isInterrupted()); //由于isInterrupted()方法清除了中断标志位,所以返回false
            }
        }
    }
    /*********************
    控制台打印如下
    true
    false
    *********************/

    4.中断标志位被清除的情况

    当一个线程处于阻塞状态时(sleep、wait、await、join),线程本身就不在运行,如果这时调用了interrupt()方法,就会抛出异常。 而抛出异常时,线程的中断标志位会被清除。

    public class Test {
        public static void main(String[] args) throws InterruptedException {
    
            Thread t = new Thread(new Task());
            t.start();
            Thread.sleep(2000);
            t.interrupt(); //设置中断标志位
        }
        private static class Task implements Runnable{
            @Override
            public void run() {
    
                try {
                    Thread.sleep(5000);
                } catch(InterruptedException ex) {
                    System.out.println(Thread.currentThread().interrupted()); //false
                    //throw new RuntimeException(ex);
                }
            }
        }
    }
    /*******************
    控制台打印如下
    false
    ********************/

    上面这段代码,在线程sleep时调用了interrupt,所以抛出了异常,异常的抛出清除了中断标志位 所以interrupted()返回false

    总结

    1.线程中断方式有stop()和interrupt()两种,其中stop()不安全已弃用

    2.interrupt()、interrupted()、isInterrupted()之间的关系

    引用

    1.《实战Java高并发程序设计》

  • 相关阅读:
    k8s资源清单创建pod
    Nginx的应用之动静分离
    Nginx的应用之虚拟主机
    Nginx的应用之安装配置
    k8s 基于NFS部署storageclass pv自动供给
    Helm入门
    k8s Pod的自动水平伸缩(HPA)
    k8s资源指标API及metrics-server资源监控
    k8s的资源限制及资源请求
    k8s的高级调度方式
  • 原文地址:https://www.cnblogs.com/fudashi/p/6568071.html
Copyright © 2020-2023  润新知