• 多线程


    总结

    java线程之间是协同式,不是抢占式
    

      

    因为是协同式,所以线程之间都是“商量”着来,最佳实践是没有谁强迫谁终止的情况的,因此才会淘汰stop()方法,详见 多线程 - 为何要弃用stop() suspend()?(不过你硬要调用stop()就另当别论...)

    • public void interrupt()  会为指定线程设置一个interrupted状态 -- 和stop()不同,不会强制线程停止,只会设置一个“interrupt标志位”,置为true(连续调用还是true,不会有异常发生。对比 Thread.start(), 多次调用会出现java.lang.IllegalStateException)。代表着“该线程被建议终止”。但是是否理会该建议,完全看该线程的具体逻辑是否去检查这个标志位,然后根据标志做出不同的反应。也有可能该线程的业务逻辑完全不理会标志位的变化,也就会继续我行我素的运行。
    • public static boolean interrupted() 会判断当前线程的interrupted状态,并且会重置该状态 -- 检查是否当前线程被设置了“interrupt标志位”。如果被设置了,返回true,反之返回false。之后,该标志位会被清除
    • public boolean isInterrupted() 会判断指定线程的interrupted状态 -- 检查是否当前线程被设置了“interrupt标志位”。如果被设置了,返回true,反之返回false。但是,该函数不会清除标志位。
        @Test
        public void testInterrupted(){
            boolean b1 = Thread.interrupted();  //false
            Thread.currentThread().interrupt();
            boolean b2 = Thread.interrupted();  //true
            boolean b3 = Thread.interrupted();  //false
        }
    
        @Test
        public void testIsInterrupted(){
            boolean b4 = Thread.currentThread().isInterrupted(); //false
            Thread.currentThread().interrupt();
            boolean b5 = Thread.currentThread().isInterrupted(); //true
            boolean b6 = Thread.currentThread().isInterrupted(); //true
        }
    

      

    调用interrupt()抛出异常的情况(必看)

    对于interrupt()方法,根据官方文档:

    • 如果指定线程调用了interrupt(),并且该线程不处于被sleep,wait,join的状态,那么一切正常,不会抛出调用InterruptException异常,就不会清除中断标志位。如果此时再调用isInterrupted()就会返回true。
    • 如果指定线程调用了interrupt()时,而指定线程正处于阻塞状态sleep,wait,join...线程自己就会立即抛InterruptException异常。
      • 原本“interrupt标志位”应该是true(因为之前调用了interrupt()),也会被清除变为false。如果再调用isInterrupted()返回false。
      • 如果你的程序不捕获这个异常,线程就会异常终止,进入TERMINATED状态;如果你的程序捕获了这个异常,那么程序就会继续执行catch语句块(可能还有finally语句块)以及以后的代码。
      • 需要注意的是,InterruptedException是线程自己从内部抛出的,并不是interrupt()方法抛出的。对某一线程调用interrupt()时,如果该线程正在执行普通的代码,那么该线程根本就不会抛出InterruptedException。但是,一旦该线程进入到wait()/sleep()/join()后,就会立刻抛出InterruptedException。

    Q:为什么当指定线程在被阻塞状态(sleep, wait)时,调用interrupt(),要立即抛出InterruptException异常,并清除“interrupt标志位”(从true变为false)

    A:【本答案不是很严谨,请大家斧正】线程在被阻塞过程中,可能掌握着某种资源。如果线程刚从阻塞回来,“interrupt标志位”就是true,很可能(视用户自定义的代码而定)线程就真准备中断,没有给程序任何回收资源的时间。因此先取消标志位,回收下资源然后视情况再决定是否真的interrupt。

    如果中断sleep,wait,join等,就会抛InterruptException异常,就会清除中断标志位,那么这种情况应该怎么处理呢?为了保证数据的一致性和完整性,我们需要用Thread.interrupt()方法再次中断自己,置上中断标志位。例子如下:

        @Test
        public void testInterrupt() throws InterruptedException {
            Thread t1 = new Thread() {
                public void run() {
                    while (true) {
                        if (Thread.currentThread().isInterrupted()) {
                            System.out.println("Interruted!");
                            break;
                        }
                        try {
                            Thread.sleep(2000); // 睡眠时中断会清除中断标志位
                        } catch (InterruptedException e) {
                            // 如果少了下面这句,这个线程虽然在外面中断,但是只要中断睡眠中的进程
                            // 就会清除中断标志位,仍然处于无限循环,会竞争CPU资源
                            Thread.currentThread().interrupt(); // 再次中断置上中断标记
                        }
                        Thread.yield();
                    }
                }
            };
            t1.start();       //t1 thread would running
            Thread.sleep(200);//sleep main thread
            t1.interrupt();   //after main thread wake up, would plan to interrupt t1 thread
        }
    

      

    interrupt()官方文档

    中断线程。
    
    如果当前线程没有中断它自己(这在任何情况下都是允许的),则该线程的 checkAccess 方法就会被调用,这可能抛出 SecurityException。
    
    如果线程在调用 Object 类的 wait()、wait(long) 或 wait(long, int) 方法,或者该类的 join()、join(long)、join(long, int)、sleep(long) 或 sleep(long, int) 方法过程中受阻,则其中断状态将被清除,它还将收到一个 InterruptedException。
    
    如果该线程在可中断的通道上的 I/O 操作中受阻,则该通道将被关闭,该线程的中断状态将被设置并且该线程将收到一个 ClosedByInterruptException。
    
    如果该线程在一个 Selector 中受阻,则该线程的中断状态将被设置,它将立即从选择操作返回,并可能带有一个非零值,就好像调用了选择器的 wakeup 方法一样。
    
    如果以前的条件都没有保存,则该线程的中断状态将被设置。
    
    中断一个不处于活动状态的线程不需要任何作用。
    
    抛出:
    
    SecurityException - 如果当前线程无法修改该线程
    

      

  • 相关阅读:
    利用opengl画一个水波的曲面
    Eclipse 使用OpenGL
    Javascript学习过程(二)
    Workflow Learing
    YAWL设计实例
    YAWL使用方法
    ImageJ二次开发学习纪录之初步体会
    [LeetCode 660] Remove 9
    [LeetCode 1542] Find Longest Awesome Substring
    [LeetCode 879] Profitable Schemes
  • 原文地址:https://www.cnblogs.com/frankcui/p/12437463.html
Copyright © 2020-2023  润新知