Java线程中断

本文主要介绍Java线程中断一些相关的概念以及注意点

Java线程的中断并不是强制的中断,调用线程中断的方法时只是起到一个通知的作用,至于线程是否要继续执行下去取决于线程自身的处理。

除去已经不推荐使用的thread.stop()方法,主要讲一下线程的成员方法thread.interrupt()、thread.isInterrupted()以及静态方法Thread.interrupted()

interrupt()

先看一下这个方法的介绍

/**                                                                          
 * Interrupts this thread.                                                   
 *                                                                           
 * <p> Unless the current thread is interrupting itself, which is            
 * always permitted, the {@link #checkAccess() checkAccess} method           
 * of this thread is invoked, which may cause a {@link                       
 * SecurityException} to be thrown.                                          
 *                                                                           
 * <p> If this thread is blocked in an invocation of the {@link              
 * Object#wait() wait()}, {@link Object#wait(long) wait(long)}, or {@link    
 * Object#wait(long, int) wait(long, int)} methods of the {@link Object}     
 * class, or of the {@link #join()}, {@link #join(long)}, {@link             
 * #join(long, int)}, {@link #sleep(long)}, or {@link #sleep(long, int)},    
 * methods of this class, then its interrupt status will be cleared and it   
 * will receive an {@link InterruptedException}.                             
 *                                                                           
 * <p> If this thread is blocked in an I/O operation upon an {@link          
 * java.nio.channels.InterruptibleChannel InterruptibleChannel}              
 * then the channel will be closed, the thread's interrupt                   
 * status will be set, and the thread will receive a {@link                  
 * java.nio.channels.ClosedByInterruptException}.                            
 *                                                                           
 * <p> If this thread is blocked in a {@link java.nio.channels.Selector}     
 * then the thread's interrupt status will be set and it will return         
 * immediately from the selection operation, possibly with a non-zero        
 * value, just as if the selector's {@link                                   
 * java.nio.channels.Selector#wakeup wakeup} method were invoked.            
 *                                                                           
 * <p> If none of the previous conditions hold then this thread's interrupt  
 * status will be set. </p>                                                  
 *                                                                           
 * <p> Interrupting a thread that is not alive need not have any effect.     
 *                                                                           
 * @throws  SecurityException                                                
 *          if the current thread cannot modify this thread                  
 *                                                                           
 * @revised 6.0                                                              
 * @spec JSR-51                                                              
 */
 public void interrupt() {                                             
    if (this != Thread.currentThread())                               
        checkAccess();                                                
                                                                      
    synchronized (blockerLock) {                                      
        Interruptible b = blocker;                                    
        if (b != null) {                                              
            interrupt0();           // Just to set the interrupt flag 
            b.interrupt(this);                                        
            return;                                                   
        }                                                             
    }                                                                 
    interrupt0();                                                     
}                                                                                                                                               

上面是官方关于interrupt()定义,大致的意思是:

线程中断自己是被允许的。除非当前线程正在中断,否则当前线程的checkAccess()会被调用,这可能会抛出SecurityException异常

当线程被Object中定义的 wait()、wait(long)或wait(long, int)以及线程中的jion()、join(long)、join(long, int)、sleep(long)或sleep(long, int)进入阻塞状态,调用interrupt()时线程的中断标记会被清除,同时抛出一个InterruptedException异常。

如果当前线程在可中断通道的I / O操作中被阻塞,则通道将被关闭,线程的中断状态将被设置为true,并且线程将收到ClosedByInterruptException。

如果当前线程在选择器中被阻塞,那么线程的中断状态将被设置为true,并且它将立即从选择操作中返回,可能具有非零值,就像调用选择器的唤醒方法一样。

如果上述情况都没有发生,那么线程的中断状态将被设置为true。

isInterrupted()、interrupted()

/**                                                                        
 * Tests whether the current thread has been interrupted.  The             
 * <i>interrupted status</i> of the thread is cleared by this method.  In  
 * other words, if this method were to be called twice in succession, the  
 * second call would return false (unless the current thread were          
 * interrupted again, after the first call had cleared its interrupted     
 * status and before the second call had examined it).                     
 *                                                                         
 * <p>A thread interruption ignored because a thread was not alive         
 * at the time of the interrupt will be reflected by this method           
 * returning false.                                                        
 *                                                                         
 * @return  <code>true</code> if the current thread has been interrupted;  
 *          <code>false</code> otherwise.                                  
 * @see #isInterrupted()                                                   
 * @revised 6.0                                                            
 */                                                                        
public static boolean interrupted() {                                      
    return currentThread().isInterrupted(true);                            
} 

上面注释主要是这个意思:

这个方法主要用于测试当前线程是否被中断,同时当前线程的中断状态会被清除。换句话说,连续调用两次这个方法,返回的结果肯定是false,当然有例外,就是第一次调用完这个方法且第二次尚未开始调用的时候,线程再次被中断,也就是中断状态再次被设置为true的情况。

当线程不存活时,线程的中断将被忽略,通过这个方法返回false来反映。

注意点是,这是Thread的静态方法

/**                                                                        
 * Tests whether this thread has been interrupted.  The <i>interrupted     
 * status</i> of the thread is unaffected by this method.                  
 *                                                                         
 * <p>A thread interruption ignored because a thread was not alive         
 * at the time of the interrupt will be reflected by this method           
 * returning false.                                                        
 *                                                                         
 * @return  <code>true</code> if this thread has been interrupted;         
 *          <code>false</code> otherwise.                                  
 * @see     #interrupted()                                                 
 * @revised 6.0                                                            
 */                                                                        
public boolean isInterrupted() {                                           
    return isInterrupted(false);                                           
}                                                                          

这个方法和上面的静态方法的主要区别是,这个成员方法调用后并不清除中断状态

线程中断

从上面的3个方法的注释可以看出,线程的中断并不是强制中断,除了可以能抛出异常的情况,都需要自己去处理。对于抛出异常情况,如果自己不想处理,最好是将异常往上传递,可以给其他用户一个处理异常的机会。如果对interrupt不处理又不反馈任何信息,就会像下面这样。

典型的synchronized对线程的中断并不处理

public class ThreadInterruptTest {

    public static void main(String[] args) {
        try {
            Object object = new Object();

            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        synchronized (object) {
                            Thread.sleep(5000);
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();

            Thread.sleep(2000);

            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    synchronized (object){
                        System.out.println("get lock");
                    }
                }
            });

            thread.start();

            Thread.sleep(1000);

            thread.interrupt();

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

5秒之后打印:

get lock

因为synchronized不处理,线程阻塞在获取锁的状态根本中断不了。

对于想中断的线程,我们可以这样操作:

public class ThreadInterruptTest {

    public static void main(String[] args) {
        try {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        while (true) {
                            Thread.sleep(1000);
                            System.out.println("in while loop");
                        }
                    } catch (InterruptedException e) {
                        System.out.println("InterruptedException");
                    }
                }
            });

            thread.start();

            Thread.sleep(4000);

            thread.interrupt();

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

打印如下:

in while loop
in while loop
in while loop
InterruptedException

正常中断了线程

再来看一种特殊情况:

public class ThreadInterruptTest {

    public static void main(String[] args) {
        try {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    while (!Thread.currentThread().isInterrupted()) {
                        try {
                            Thread.sleep(1000);
                            System.out.println("in while loop");
                        } catch (InterruptedException e) {
                            System.out.println("InterruptedException");
                        }
                    }
                }
            });

            thread.start();

            Thread.sleep(4000);

            thread.interrupt();

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

打印是这样的:

in while loop
in while loop
in while loop
InterruptedException
in while loop
in while loop
in while loop
in while loop
...

程序进入了死循环,猜到什么原因了吗?上面关于thread.interrupt()的注释里面明确说明了,当抛出InterruptedException异常时,线程的中断标记会被清除!!!这就是引起死循环的原因。我们可以这样处理,在捕捉到异常后后加上Thread.currentThread().isInterrupted();再次设置中断的标记。或者像最开始的写法,将整个while循环放try里面。

当然也可以通过自己维护flag的形式来中断线程

public class ThreadInterruptTest {

    private static volatile boolean interruptFlag = false;

    public static void main(String[] args) {
        try {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    while (!interruptFlag) {
                        System.out.println("in while loop");
                    }

                    System.out.println("interrupted");
                }
            }) {
                @Override
                public void interrupt() {
                    super.interrupt();
                    interruptFlag = true;
                }
            };

            thread.start();

            Thread.sleep(2000);

            thread.interrupt();

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

最后看一下调用interrupted(),清除中断标记的情况

public class ThreadInterruptTest {

    public static void main(String[] args) {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.interrupted());
                System.out.println(Thread.interrupted());
            }
        });

        thread.start();
        thread.interrupt();
    }
}

打印如下:

true
false

总结

  1. 线程调用interrupt()之后并一定会中断,例如在等待synchronized锁的状态下
  2. 线程在sleep()、wait()、jion()等阻塞状态下被中断会抛出InterruptedException异常,同时中断标记被清除
  3. 连续调用两次静态方法interrupted()第二次将返回false(特殊情况注意一下)
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容

  • 1.sleep 让线程睡眠 通过源码可以看出,最终sleep的时间粒度是毫秒数,虽然第一个方法存在有纳秒级的参数,...
    酱油和醋阅读 1,005评论 0 0
  • 单任务 单任务的特点是排队执行,也就是同步,就像再cmd输入一条命令后,必须等待这条命令执行完才可以执行下一条命令...
    Steven1997阅读 1,172评论 0 6
  • 1、为什么废弃Thread的stop函数? 对于有多线程开发经验的开发者,应该大多数在开发过程中都遇到过这样的需求...
    萧雾宇阅读 10,516评论 2 21
  • 第5章 多线程编程 5.1 线程基础 5.1.1 如何创建线程 在java要创建线程,一般有==两种方式==:1)...
    AndroidMaster阅读 1,789评论 0 11
  • 毫无头绪……
    泼茶人阅读 168评论 0 0