[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"consumer-news-detail-1012":3,"consumer-news-interaction-1012":40,"consumer-news-related-1012":43},{"detail":4,"item":35},{"card":5,"schemaVersion":22,"fields":23,"content":29},{"id":6,"kind":7,"targetType":8,"targetId":9,"subtype":7,"typeLabel":10,"title":11,"subtitle":12,"summary":13,"coverUrl":14,"badgeText":14,"href":15,"sourceName":12,"meta":16,"metrics":19,"tags":20,"resolved":21},"NEWS_ARTICLE:1012","news","NEWS_ARTICLE",1012,"资讯","java 多线程开发系列之八：玩转多线程（线程的中断）","博客园","之前的线程协作，讲的是通过wait和notify方法，多线程之间进行互相条件唤醒的办法。除此之外我们还需要进行中断操作。等待和唤醒可参考前文https:\u002F\u002Fwww.cnblogs.com\u002Fjilodream\u002Fp\u002F22770136设想这样一个场景：长工在给地主家干活，日出而作，日落而息。思路很简单：","","\u002Fnews\u002F1012",[17,18],"2026","软件开发",{},[18],true,"consumer-content-detail-v1",{"sourceName":12,"authorName":24,"categoryName":18,"summary":13,"description":13,"publishTime":25,"updateTime":26,"sourceUrl":27,"language":28},"王若伊_恩赐解脱","2026-09-07T10:15","2026-09-11T15:22:03","https:\u002F\u002Fwww.cnblogs.com\u002Fjilodream\u002Fp\u002F22869617","中文",{"format":30,"policy":31,"normalized":21,"html":32,"text":33,"wordCount":34,"hasBody":21},"HTML","NEWS_CONTENT_V1","\u003Cp>\u003Cspan>之前的线程协作，讲的是通过wait和notify方法，多线程之间进行互相条件唤醒的办法。除此之外我们还需要进行中断操作。\u003C\u002Fspan>\u003Cbr>\u003Cspan>\u003Cem>等待和唤醒可参考前文https:\u002F\u002Fwww.cnblogs.com\u002Fjilodream\u002Fp\u002F22770136\u003C\u002Fem>\u003C\u002Fspan>\u003Cbr>\u003Cspan>设想这样一个场景：长工在给地主家干活，日出而作，日落而息。\u003C\u002Fspan>\u003Cbr>\u003Cspan>思路很简单： 长工线程的伪代码\u003C\u002Fspan>\u003C\u002Fp>\n\u003Cdiv>\n \u003Cpre>\u003Cspan> 1\u003C\u002Fspan>   \u003Cspan>public\u003C\u002Fspan> \u003Cspan>void\u003C\u002Fspan>\u003Cspan> run (){\n\u003C\u002Fspan>\u003Cspan> 2\u003C\u002Fspan>       \u003Cspan>while\u003C\u002Fspan>(\u003Cspan>true\u003C\u002Fspan>\u003Cspan>) {\n\u003C\u002Fspan>\u003Cspan> 3\u003C\u002Fspan>       \u003Cspan>if\u003C\u002Fspan>\u003Cspan> (time 早于 日出){\n\u003C\u002Fspan>\u003Cspan> 4\u003C\u002Fspan>           \u003Cspan>\u002F\u002F\u003C\u002Fspan>\u003Cspan>继续休息\n\u003C\u002Fspan>\u003Cspan> 5\u003C\u002Fspan>           \u003Cspan>\u002F\u002F\u003C\u002Fspan>\u003Cspan>continue;\u003C\u002Fspan>\n\u003Cspan> 6\u003C\u002Fspan> \u003Cspan>      }\n\u003C\u002Fspan>\u003Cspan> 7\u003C\u002Fspan>       \u003Cspan>if\u003C\u002Fspan>\u003Cspan> (time 早于 日落){\n\u003C\u002Fspan>\u003Cspan> 8\u003C\u002Fspan>           \u003Cspan>\u002F\u002F\u003C\u002Fspan>\u003Cspan>继续工作\n\u003C\u002Fspan>\u003Cspan> 9\u003C\u002Fspan>           \u003Cspan>\u002F\u002F\u003C\u002Fspan>\u003Cspan>continue;\u003C\u002Fspan>\n\u003Cspan>10\u003C\u002Fspan> \u003Cspan>      }\n\u003C\u002Fspan>\u003Cspan>11\u003C\u002Fspan>       \u003Cspan>\u002F\u002F\u003C\u002Fspan>\u003Cspan>时间属于日落\n\u003C\u002Fspan>\u003Cspan>12\u003C\u002Fspan>       \u003Cspan>\u002F\u002F\u003C\u002Fspan>\u003Cspan>结束工作\n\u003C\u002Fspan>\u003Cspan>13\u003C\u002Fspan>       \u003Cspan>\u002F\u002F\u003C\u002Fspan>\u003Cspan>return;\u003C\u002Fspan>\n\u003Cspan>14\u003C\u002Fspan> \u003Cspan>     }\n\u003C\u002Fspan>\u003Cspan>15\u003C\u002Fspan>   }\u003C\u002Fpre>\n\u003C\u002Fdiv>\n\u003Cp>\u003Cspan>这就是最典型的通过标志位状态来中断线程的操作。除了开发人员之外，java本身也给线程类内置了一个中断状态的标记变量。\u003C\u002Fspan>\u003C\u002Fp>\n\u003Cdiv>\n \u003Cpre> \u003Cspan>\u002F*\u003C\u002Fspan>\u003Cspan> Interrupt state of the thread - read\u002Fwritten directly by JVM \u003C\u002Fspan>\u003Cspan>*\u002F\u003C\u002Fspan>\n    \u003Cspan>private\u003C\u002Fspan> \u003Cspan>volatile\u003C\u002Fspan> \u003Cspan>boolean\u003C\u002Fspan> interrupted;\u003C\u002Fpre>\n\u003C\u002Fdiv>\n\u003Cp>\u003Cspan>注释的意思：这是线程的中断状态，读写都是由JVM直接操纵的。\u003C\u002Fspan>\u003Cbr>\u003Cspan>线程可以通过这个标记，来check自身的状态。并且针对这个变量，jdk提供了几个核心方法，供开发人员直接使用：\u003C\u002Fspan>\u003Cbr>\u003Cspan>\u003Cstrong>（1）中断这个线程，注意这是实例方法，所中断的线程也就是这个线程实例。\u003C\u002Fstrong>\u003C\u002Fspan>\u003C\u002Fp>\n\u003Cp>\u003Cspan>\u003Cstrong>&#xa0; &#xa0; public void interrupt()&#xa0;\u003C\u002Fstrong>\u003C\u002Fspan>\u003C\u002Fp>\n\u003Cp>\u003Cspan>\u003Cstrong>（2）查看这个线程的中断标记，注意这是实例方法，所查看的线程标记变量也就是这个线程实例内置的中断标记。\u003C\u002Fstrong>\u003C\u002Fspan>\u003C\u002Fp>\n\u003Cp>\u003Cspan>\u003Cstrong>&#xa0; &#xa0; public boolean isInterrupted()\u003C\u002Fstrong>\u003C\u002Fspan>\u003C\u002Fp>\n\u003Cp>\u003Cspan>\u003Cstrong>（3）查看线程的标记变量，注意这是一个静态方法，其所中断的线程也就是当前执行这个方法的线程currentThread。并且这个方法会清除标记，因此作者在命名时加了ed，表示是否过去被中断过。\u003C\u002Fstrong>\u003C\u002Fspan>\u003C\u002Fp>\n\u003Cp>\u003Cspan>\u003Cstrong>&#xa0; &#xa0; public static boolean interrupted()\u003C\u002Fstrong>\u003C\u002Fspan>\u003C\u002Fp>\n\u003Cp>\u003Cspan>&#xa0; &#xa0; 顺便依次看下源码：\u003C\u002Fspan>\u003C\u002Fp>\n\u003Cdiv>\n \u003Cpre>\u003Cspan> 1\u003C\u002Fspan>     \u003Cspan>public\u003C\u002Fspan> \u003Cspan>void\u003C\u002Fspan>\u003Cspan> interrupt() {\n\u003C\u002Fspan>\u003Cspan> 2\u003C\u002Fspan>         \u003Cspan>if\u003C\u002Fspan> (\u003Cspan>this\u003C\u002Fspan> !=\u003Cspan> Thread.currentThread()) {\n\u003C\u002Fspan>\u003Cspan> 3\u003C\u002Fspan> \u003Cspan>            checkAccess();\n\u003C\u002Fspan>\u003Cspan> 4\u003C\u002Fspan> \n\u003Cspan> 5\u003C\u002Fspan>             \u003Cspan>\u002F\u002F\u003C\u002Fspan>\u003Cspan> thread may be blocked in an I\u002FO operation\u003C\u002Fspan>\n\u003Cspan> 6\u003C\u002Fspan>             \u003Cspan>synchronized\u003C\u002Fspan>\u003Cspan> (blockerLock) {\n\u003C\u002Fspan>\u003Cspan> 7\u003C\u002Fspan>                 Interruptible b =\u003Cspan> blocker;\n\u003C\u002Fspan>\u003Cspan> 8\u003C\u002Fspan>                 \u003Cspan>if\u003C\u002Fspan> (b != \u003Cspan>null\u003C\u002Fspan>\u003Cspan>) {\n\u003C\u002Fspan>\u003Cspan> 9\u003C\u002Fspan>                     interrupted = \u003Cspan>true\u003C\u002Fspan>\u003Cspan>;\n\u003C\u002Fspan>\u003Cspan>10\u003C\u002Fspan>                     interrupt0();  \u003Cspan>\u002F\u002F\u003C\u002Fspan>\u003Cspan> inform VM of interrupt\u003C\u002Fspan>\n\u003Cspan>11\u003C\u002Fspan>                     b.interrupt(\u003Cspan>this\u003C\u002Fspan>\u003Cspan>);\n\u003C\u002Fspan>\u003Cspan>12\u003C\u002Fspan>                     \u003Cspan>return\u003C\u002Fspan>\u003Cspan>;\n\u003C\u002Fspan>\u003Cspan>13\u003C\u002Fspan> \u003Cspan>                }\n\u003C\u002Fspan>\u003Cspan>14\u003C\u002Fspan> \u003Cspan>            }\n\u003C\u002Fspan>\u003Cspan>15\u003C\u002Fspan> \u003Cspan>        }\n\u003C\u002Fspan>\u003Cspan>16\u003C\u002Fspan>         interrupted = \u003Cspan>true\u003C\u002Fspan>\u003Cspan>;\n\u003C\u002Fspan>\u003Cspan>17\u003C\u002Fspan>         \u003Cspan>\u002F\u002F\u003C\u002Fspan>\u003Cspan> inform VM of interrupt\u003C\u002Fspan>\n\u003Cspan>18\u003C\u002Fspan> \u003Cspan>        interrupt0();\n\u003C\u002Fspan>\u003Cspan>19\u003C\u002Fspan> \u003Cspan>    }\n\u003C\u002Fspan>\u003Cspan>20\u003C\u002Fspan>     \n\u003Cspan>21\u003C\u002Fspan>     \u003Cspan>private\u003C\u002Fspan> \u003Cspan>native\u003C\u002Fspan> \u003Cspan>void\u003C\u002Fspan> interrupt0();\u003C\u002Fpre>\n\u003C\u002Fdiv>\n\u003Cp>\u003Cspan> 这个方法的大致逻辑是：\u003C\u002Fspan>\u003Cbr>\u003Cspan> 先判断中断当前方法的线程是不是就是当前线程，\u003C\u002Fspan>\u003Cbr>\u003Cspan> 如果不是话，check一下安全设置，检查是否可以跨线程中断其它线程，如果有问题这里会抛出一个运行时异常 SecurityException。如果安全检查也没问题，判断当前线程是否处于I\u002FO阻塞中。如果是的话就打断线程的I\u002FO阻塞（阻塞的地方通常会抛出IOException相关的异常。\u003C\u002Fspan>\u003C\u002Fp>\n\u003Cp>\u003Cspan>另外通常只有nio 的阻塞才会中断，传统的io不会响应中断，read write 方法依然会阻塞住），设置标记位interrupted为true，表示被中断了。并且调用native 方法interrupt0，让jvm来处理中断的具体操作。\u003C\u002Fspan>\u003Cbr>\u003Cspan> 如果是当前线程，或者没有处于阻塞中的话，(防盗连接：本文首发自http:\u002F\u002Fwww.cnblogs.com\u002Fjilodream\u002F )这里直接设置标记位interrupted为true，并且调用native 方法interrupt0即可。\u003C\u002Fspan>\u003C\u002Fp>\n\u003Cp>\u003Cspan>然后是获取中断状态，这里很简单，直接拿状态变量：\u003C\u002Fspan>\u003C\u002Fp>\n\u003Cdiv>\n \u003Cpre>\u003Cspan>1\u003C\u002Fspan>     \u003Cspan>public\u003C\u002Fspan> \u003Cspan>boolean\u003C\u002Fspan>\u003Cspan> isInterrupted() {\n\u003C\u002Fspan>\u003Cspan>2\u003C\u002Fspan>         \u003Cspan>return\u003C\u002Fspan>\u003Cspan> interrupted;\n\u003C\u002Fspan>\u003Cspan>3\u003C\u002Fspan>     }\u003C\u002Fpre>\n\u003C\u002Fdiv>\n\u003Cp>\u003Cspan>接着是获取并清理中断状态：\u003C\u002Fspan>\u003C\u002Fp>\n\u003Cdiv>\n \u003Cpre>\u003Cspan> 1\u003C\u002Fspan>     \u003Cspan>public\u003C\u002Fspan> \u003Cspan>static\u003C\u002Fspan> \u003Cspan>boolean\u003C\u002Fspan>\u003Cspan> interrupted() {\n\u003C\u002Fspan>\u003Cspan> 2\u003C\u002Fspan>         Thread t =\u003Cspan> currentThread();\n\u003C\u002Fspan>\u003Cspan> 3\u003C\u002Fspan>         \u003Cspan>boolean\u003C\u002Fspan> interrupted =\u003Cspan> t.interrupted;\n\u003C\u002Fspan>\u003Cspan> 4\u003C\u002Fspan>         \u003Cspan>\u002F\u002F\u003C\u002Fspan>\u003Cspan> We may have been interrupted the moment after we read the field,\n\u003C\u002Fspan>\u003Cspan> 5\u003C\u002Fspan>         \u003Cspan>\u002F\u002F\u003C\u002Fspan>\u003Cspan> so only clear the field if we saw that it was set and will return\n\u003C\u002Fspan>\u003Cspan> 6\u003C\u002Fspan>         \u003Cspan>\u002F\u002F\u003C\u002Fspan>\u003Cspan> true; otherwise we could lose an interrupt.\u003C\u002Fspan>\n\u003Cspan> 7\u003C\u002Fspan>         \u003Cspan>if\u003C\u002Fspan>\u003Cspan> (interrupted) {\n\u003C\u002Fspan>\u003Cspan> 8\u003C\u002Fspan>             t.interrupted = \u003Cspan>false\u003C\u002Fspan>\u003Cspan>;\n\u003C\u002Fspan>\u003Cspan> 9\u003C\u002Fspan> \u003Cspan>            clearInterruptEvent();\n\u003C\u002Fspan>\u003Cspan>10\u003C\u002Fspan> \u003Cspan>        }\n\u003C\u002Fspan>\u003Cspan>11\u003C\u002Fspan>         \u003Cspan>return\u003C\u002Fspan>\u003Cspan> interrupted;\n\u003C\u002Fspan>\u003Cspan>12\u003C\u002Fspan> \u003Cspan>    }\n\u003C\u002Fspan>\u003Cspan>13\u003C\u002Fspan> \n\u003Cspan>14\u003C\u002Fspan>     \u003Cspan>private\u003C\u002Fspan> \u003Cspan>static\u003C\u002Fspan> \u003Cspan>native\u003C\u002Fspan> \u003Cspan>void\u003C\u002Fspan> clearInterruptEvent();\u003C\u002Fpre>\n\u003C\u002Fdiv>\n\u003Cp>\u003Cspan>核心逻辑是：取出当前线程t，以及当前线程的中断状态，如果当前线程的中断标记位true。那么就设置其为false，\u003Cstrong>\u003Cspan>并且清理中断事件：告诉jvm，底层未处理的pending状态的中断信号清理掉。（后文红色字体会说到）\u003C\u002Fspan>\u003C\u002Fstrong>\u003C\u002Fspan>\u003C\u002Fp>\n\u003Cp>\u003Cspan>知道了这些api的作用，我们来看一个简单的例子：\u003C\u002Fspan>\u003C\u002Fp>\n\u003Cdiv>\n \u003Cpre>\u003Cspan> 1\u003C\u002Fspan> \u003Cspan>public\u003C\u002Fspan> \u003Cspan>class\u003C\u002Fspan>\u003Cspan> InterruptStudy {\n\u003C\u002Fspan>\u003Cspan> 2\u003C\u002Fspan>     \u003Cspan>public\u003C\u002Fspan> \u003Cspan>static\u003C\u002Fspan> \u003Cspan>void\u003C\u002Fspan> main(String[] args) \u003Cspan>throws\u003C\u002Fspan>\u003Cspan> InterruptedException {\n\u003C\u002Fspan>\u003Cspan> 3\u003C\u002Fspan> \n\u003Cspan> 4\u003C\u002Fspan> \n\u003Cspan> 5\u003C\u002Fspan>         Thread t1 = \u003Cspan>new\u003C\u002Fspan> Thread(() -&gt;\u003Cspan> {\n\u003C\u002Fspan>\u003Cspan> 6\u003C\u002Fspan>             \u003Cspan>while\u003C\u002Fspan> (!\u003Cspan>Thread.currentThread().isInterrupted()) {\n\u003C\u002Fspan>\u003Cspan> 7\u003C\u002Fspan>                 System.out.println(\"do sth\"\u003Cspan>);\n\u003C\u002Fspan>\u003Cspan> 8\u003C\u002Fspan> \u003Cspan>            }\n\u003C\u002Fspan>\u003Cspan> 9\u003C\u002Fspan>             System.out.println(\"task end, interrupt state \" +\u003Cspan> Thread.currentThread().isInterrupted());\n\u003C\u002Fspan>\u003Cspan>10\u003C\u002Fspan>             \u003Cspan>return\u003C\u002Fspan>\u003Cspan>;\n\u003C\u002Fspan>\u003Cspan>11\u003C\u002Fspan> \u003Cspan>        });\n\u003C\u002Fspan>\u003Cspan>12\u003C\u002Fspan> \n\u003Cspan>13\u003C\u002Fspan> \u003Cspan>        t1.start();\n\u003C\u002Fspan>\u003Cspan>14\u003C\u002Fspan>         Thread.sleep(1000L\u003Cspan>);\n\u003C\u002Fspan>\u003Cspan>15\u003C\u002Fspan> \u003Cspan>        t1.interrupt();\n\u003C\u002Fspan>\u003Cspan>16\u003C\u002Fspan> \u003Cspan>        t1.join();\n\u003C\u002Fspan>\u003Cspan>17\u003C\u002Fspan> \u003Cspan>    }\n\u003C\u002Fspan>\u003Cspan>18\u003C\u002Fspan> \n\u003Cspan>19\u003C\u002Fspan> }\u003C\u002Fpre>\n\u003C\u002Fdiv>\n\u003Cp>\u003Cspan>输出结果如下：\u003C\u002Fspan>\u003C\u002Fp>\n\u003Cdiv>\n \u003Cpre>Connected to the target VM, address: '127.0.0.1:53895', transport: 'socket'\n\u003Cspan>do\u003C\u002Fspan>\u003Cspan> sth\n\u003C\u002Fspan>\u003Cspan>do\u003C\u002Fspan>\u003Cspan> sth\n\u003C\u002Fspan>\u003Cspan>do\u003C\u002Fspan>\u003Cspan> sth\n\u003C\u002Fspan>\u003Cspan>do\u003C\u002Fspan>\u003Cspan> sth\n....\n\u003C\u002Fspan>\u003Cspan>do\u003C\u002Fspan>\u003Cspan> sth\n\u003C\u002Fspan>\u003Cspan>do\u003C\u002Fspan>\u003Cspan> sth\n\u003C\u002Fspan>\u003Cspan>do\u003C\u002Fspan>\u003Cspan> sth\n\u003C\u002Fspan>\u003Cspan>do\u003C\u002Fspan>\u003Cspan> sth\ntask end, interrupt state \u003C\u002Fspan>\u003Cspan>true\u003C\u002Fspan>\u003Cspan>\nDisconnected from the target VM, address: \u003C\u002Fspan>'127.0.0.1:53895', transport: 'socket'\u003Cspan>\n\nProcess finished with exit code \u003C\u002Fspan>0\u003C\u002Fpre>\n\u003C\u002Fdiv>\n\u003Cp>\u003Cspan>这样子就可以通过中断标记，和线程进行交互从而中断线程了。\u003C\u002Fspan>\u003C\u002Fp>\n\u003Cp>\u003Cspan>再改为使用静态方法：\u003C\u002Fspan>\u003C\u002Fp>\n\u003Cdiv>\n \u003Cpre>\u003Cspan> 1\u003C\u002Fspan> \u003Cspan>public\u003C\u002Fspan> \u003Cspan>class\u003C\u002Fspan>\u003Cspan> InterruptStudy {\n\u003C\u002Fspan>\u003Cspan> 2\u003C\u002Fspan>     \u003Cspan>public\u003C\u002Fspan> \u003Cspan>static\u003C\u002Fspan> \u003Cspan>void\u003C\u002Fspan> main(String[] args) \u003Cspan>throws\u003C\u002Fspan>\u003Cspan> InterruptedException {\n\u003C\u002Fspan>\u003Cspan> 3\u003C\u002Fspan> \n\u003Cspan> 4\u003C\u002Fspan> \n\u003Cspan> 5\u003C\u002Fspan>         Thread t1 = \u003Cspan>new\u003C\u002Fspan> Thread(() -&gt;\u003Cspan> {\n\u003C\u002Fspan>\u003Cspan> 6\u003C\u002Fspan>             \u003Cspan>while\u003C\u002Fspan> (!\u003Cspan>Thread.interrupted()) {\n\u003C\u002Fspan>\u003Cspan> 7\u003C\u002Fspan>                 System.out.println(\"do sth\"\u003Cspan>);\n\u003C\u002Fspan>\u003Cspan> 8\u003C\u002Fspan> \u003Cspan>            }\n\u003C\u002Fspan>\u003Cspan> 9\u003C\u002Fspan>             System.out.println(\"task end, interrupt state \" +\u003Cspan> Thread.currentThread().isInterrupted());\n\u003C\u002Fspan>\u003Cspan>10\u003C\u002Fspan>             \u003Cspan>return\u003C\u002Fspan>\u003Cspan>;\n\u003C\u002Fspan>\u003Cspan>11\u003C\u002Fspan> \u003Cspan>        });\n\u003C\u002Fspan>\u003Cspan>12\u003C\u002Fspan> \n\u003Cspan>13\u003C\u002Fspan> \u003Cspan>        t1.start();\n\u003C\u002Fspan>\u003Cspan>14\u003C\u002Fspan>         Thread.sleep(10L\u003Cspan>);\n\u003C\u002Fspan>\u003Cspan>15\u003C\u002Fspan> \u003Cspan>        t1.interrupt();\n\u003C\u002Fspan>\u003Cspan>16\u003C\u002Fspan> \u003Cspan>        t1.join();\n\u003C\u002Fspan>\u003Cspan>17\u003C\u002Fspan> \u003Cspan>    }\n\u003C\u002Fspan>\u003Cspan>18\u003C\u002Fspan> \n\u003Cspan>19\u003C\u002Fspan> }\u003C\u002Fpre>\n\u003C\u002Fdiv>\n\u003Cp>\u003Cspan>输出结果如下：\u003C\u002Fspan>\u003C\u002Fp>\n\u003Cdiv>\n \u003Cpre>Connected to the target VM, address: '127.0.0.1:60872', transport: 'socket'\n\u003Cspan>do\u003C\u002Fspan>\u003Cspan> sth\n\u003C\u002Fspan>\u003Cspan>do\u003C\u002Fspan>\u003Cspan> sth\n\u003C\u002Fspan>\u003Cspan>do\u003C\u002Fspan>\u003Cspan> sth\n....\n\u003C\u002Fspan>\u003Cspan>do\u003C\u002Fspan>\u003Cspan> sth\n\u003C\u002Fspan>\u003Cspan>do\u003C\u002Fspan>\u003Cspan> sth\n\u003C\u002Fspan>\u003Cspan>do\u003C\u002Fspan>\u003Cspan> sth\n\u003C\u002Fspan>\u003Cspan>do\u003C\u002Fspan>\u003Cspan> sth\n\u003C\u002Fspan>\u003Cspan>do\u003C\u002Fspan>\u003Cspan> sth\n\u003C\u002Fspan>\u003Cspan>do\u003C\u002Fspan>\u003Cspan> sth\n\u003C\u002Fspan>\u003Cspan>do\u003C\u002Fspan>\u003Cspan> sth\ntask end, interrupt state \u003C\u002Fspan>\u003Cspan>false\u003C\u002Fspan>\u003Cspan>\nDisconnected from the target VM, address: \u003C\u002Fspan>'127.0.0.1:60872', transport: 'socket'\u003Cspan>\n\nProcess finished with exit code \u003C\u002Fspan>0\u003C\u002Fpre>\n\u003C\u002Fdiv>\n\u003Cp>\u003Cspan>发现中断以后，重置了中断标记，因此就返回false。\u003C\u002Fspan>\u003C\u002Fp>\n\u003Cp>\u003Cspan>但是java的中断能力远不止此，如果仅仅是这样，那也太容易了。还记得上篇文章的wait么，以及更早之前的sleep，如果线程处于这种状态，(防盗连接：本文首发自http:\u002F\u002Fwww.cnblogs.com\u002Fjilodream\u002F )无法自行的check 中断状态怎么办。没关系jvm 会自行的帮你唤醒，抛出一个InterruptedException。\u003C\u002Fspan>\u003Cbr>\u003Cspan>同时在抛出异常之前，jvm还会帮你做两件事：清理标记，重新尝试获取锁。\u003C\u002Fspan>\u003Cbr>\u003Cspan>请看下边这个例子：\u003C\u002Fspan>\u003C\u002Fp>\n\u003Cdiv>\n \u003Cpre>\u003Cspan> 1\u003C\u002Fspan> \u003Cspan>public\u003C\u002Fspan> \u003Cspan>class\u003C\u002Fspan>\u003Cspan> InterruptExStudy {\n\u003C\u002Fspan>\u003Cspan> 2\u003C\u002Fspan>     \u003Cspan>private\u003C\u002Fspan> \u003Cspan>static\u003C\u002Fspan> \u003Cspan>final\u003C\u002Fspan> Object lock = \u003Cspan>new\u003C\u002Fspan>\u003Cspan> Object();\n\u003C\u002Fspan>\u003Cspan> 3\u003C\u002Fspan> \n\u003Cspan> 4\u003C\u002Fspan>     \u003Cspan>public\u003C\u002Fspan> \u003Cspan>static\u003C\u002Fspan> \u003Cspan>void\u003C\u002Fspan> main(String[] args) \u003Cspan>throws\u003C\u002Fspan>\u003Cspan> InterruptedException {\n\u003C\u002Fspan>\u003Cspan> 5\u003C\u002Fspan> \n\u003Cspan> 6\u003C\u002Fspan> \n\u003Cspan> 7\u003C\u002Fspan>         Thread t1 = \u003Cspan>new\u003C\u002Fspan> Thread(() -&gt;\u003Cspan> {\n\u003C\u002Fspan>\u003Cspan> 8\u003C\u002Fspan>             \u003Cspan>synchronized\u003C\u002Fspan>\u003Cspan> (lock) {\n\u003C\u002Fspan>\u003Cspan> 9\u003C\u002Fspan>                 System.out.println(\"task  start\"\u003Cspan>);\n\u003C\u002Fspan>\u003Cspan>10\u003C\u002Fspan>                 \u003Cspan>try\u003C\u002Fspan>\u003Cspan> {\n\u003C\u002Fspan>\u003Cspan>11\u003C\u002Fspan>                     System.out.println(\"task  do sth\"\u003Cspan>);\n\u003C\u002Fspan>\u003Cspan>12\u003C\u002Fspan> \u003Cspan>                    lock.wait();\n\u003C\u002Fspan>\u003Cspan>13\u003C\u002Fspan>                 } \u003Cspan>catch\u003C\u002Fspan>\u003Cspan> (InterruptedException e) {\n\u003C\u002Fspan>\u003Cspan>14\u003C\u002Fspan>                     System.out.println(\"task catch interrupt ex ,state: \" +\u003Cspan> Thread.currentThread().isInterrupted());\n\u003C\u002Fspan>\u003Cspan>15\u003C\u002Fspan> \u003Cspan>                }\n\u003C\u002Fspan>\u003Cspan>16\u003C\u002Fspan>                 System.out.println(\"task end \"\u003Cspan>);\n\u003C\u002Fspan>\u003Cspan>17\u003C\u002Fspan> \u003Cspan>            }\n\u003C\u002Fspan>\u003Cspan>18\u003C\u002Fspan> \u003Cspan>        });\n\u003C\u002Fspan>\u003Cspan>19\u003C\u002Fspan> \n\u003Cspan>20\u003C\u002Fspan> \u003Cspan>        t1.start();\n\u003C\u002Fspan>\u003Cspan>21\u003C\u002Fspan>         Thread.sleep(1000L\u003Cspan>);\n\u003C\u002Fspan>\u003Cspan>22\u003C\u002Fspan>         \u003Cspan>synchronized\u003C\u002Fspan>\u003Cspan> (lock) {\n\u003C\u002Fspan>\u003Cspan>23\u003C\u002Fspan>             System.out.println(\"before notify, t1 state-0 :\" +\u003Cspan> t1.isInterrupted());\n\u003C\u002Fspan>\u003Cspan>24\u003C\u002Fspan> \u003Cspan>            t1.interrupt();\n\u003C\u002Fspan>\u003Cspan>25\u003C\u002Fspan>             System.out.println(\"before notify, t1 state-1 :\" +\u003Cspan> t1.isInterrupted());\n\u003C\u002Fspan>\u003Cspan>26\u003C\u002Fspan> \u003Cspan>        }\n\u003C\u002Fspan>\u003Cspan>27\u003C\u002Fspan> \u003Cspan>        t1.join();\n\u003C\u002Fspan>\u003Cspan>28\u003C\u002Fspan> \u003Cspan>    }\n\u003C\u002Fspan>\u003Cspan>29\u003C\u002Fspan> \n\u003Cspan>30\u003C\u002Fspan> }\u003C\u002Fpre>\n\u003C\u002Fdiv>\n\u003Cp>\u003Cspan>输出如下：\u003C\u002Fspan>\u003C\u002Fp>\n\u003Cdiv>\n \u003Cpre>Connected to the target VM, address: '127.0.0.1:51128', transport: 'socket'\u003Cspan>\ntask  start\ntask  \u003C\u002Fspan>\u003Cspan>do\u003C\u002Fspan>\u003Cspan> sth\nbefore notify, t1 state\u003C\u002Fspan>-0 :\u003Cspan>false\u003C\u002Fspan>\u003Cspan>\nbefore notify, t1 state\u003C\u002Fspan>-1 :\u003Cspan>true\u003C\u002Fspan>\u003Cspan>\ntask \u003C\u002Fspan>\u003Cspan>catch\u003C\u002Fspan> interrupt ex ,state: \u003Cspan>false\u003C\u002Fspan>\u003Cspan>\ntask end \nDisconnected from the target VM, address: \u003C\u002Fspan>'127.0.0.1:51128', transport: 'socket'\u003Cspan>\n\nProcess finished with exit code \u003C\u002Fspan>0\u003C\u002Fpre>\n\u003C\u002Fdiv>\n\u003Cp>\u003Cspan>大致逻辑是这样的：\u003C\u002Fspan>\u003C\u002Fp>\n\u003Cp>\u003Cspan>主线程启动t1线程，t1线程抢到锁，t1处于wait状态（同时释放锁）， 主线程抢到锁，主线程中断t1，t1需要再次抢到锁才能抛出中断异常，\u003C\u002Fspan>\u003C\u002Fp>\n\u003Cp>\u003Cspan>当后续在抛出中断异常的时候，t1内部的中断标记也被重置了。\u003C\u002Fspan>\u003C\u002Fp>\n\u003Cp>\u003Cspan>\u003Cstrong>这样子即使线程处于join\u002Fwait\u002Fsleep等状态下，无法主动check状态，我们也可以通过抛出异常的形式，来中断线程了。同时根据前文中的jdk源码，也可以知道，即使在IO等待的状态下，也会被中断。而一开始说的api：interrupted()中的源码中，会调用clearInterruptEvent() 清理中断事件，其作用就是为了清理这些中断异常抛出等事件，保证虽然标记被恢复为false，对应的中断事件也都被清理干净了。\u003C\u002Fstrong>\u003C\u002Fspan>\u003C\u002Fp>\n\u003Cp>\u003Cspan>\u003Cspan>\u003Cstrong>另外还需要注意的是，中断是一个累加状态，也就是说即使当前线程还没开始阻塞（join\u002Fwait\u002Fsleep\u002Fio阻塞等），一旦被标记为中断，即使后边再遇到阻塞场景，也一样被中断。\u003C\u002Fstrong>\u003C\u002Fspan>来看这个例子：\u003C\u002Fspan>\u003C\u002Fp>\n\u003Cdiv>\n \u003Cpre>\u003Cspan> 1\u003C\u002Fspan> \u003Cspan>import\u003C\u002Fspan>\u003Cspan> java.util.concurrent.TimeUnit;\n\u003C\u002Fspan>\u003Cspan> 2\u003C\u002Fspan> \n\u003Cspan> 3\u003C\u002Fspan> \u003Cspan>\u002F**\u003C\u002Fspan>\n\u003Cspan> 4\u003C\u002Fspan> \u003Cspan> * @discription\n\u003C\u002Fspan>\u003Cspan> 5\u003C\u002Fspan>  \u003Cspan>*\u002F\u003C\u002Fspan>\n\u003Cspan> 6\u003C\u002Fspan> \u003Cspan>public\u003C\u002Fspan> \u003Cspan>class\u003C\u002Fspan>\u003Cspan> InterruptEx1Study {\n\u003C\u002Fspan>\u003Cspan> 7\u003C\u002Fspan>     \u003Cspan>private\u003C\u002Fspan> \u003Cspan>static\u003C\u002Fspan> \u003Cspan>final\u003C\u002Fspan> Object lock = \u003Cspan>new\u003C\u002Fspan>\u003Cspan> Object();\n\u003C\u002Fspan>\u003Cspan> 8\u003C\u002Fspan> \n\u003Cspan> 9\u003C\u002Fspan>     \u003Cspan>static\u003C\u002Fspan> \u003Cspan>volatile\u003C\u002Fspan> \u003Cspan>boolean\u003C\u002Fspan> time_out = \u003Cspan>false\u003C\u002Fspan>\u003Cspan>;\n\u003C\u002Fspan>\u003Cspan>10\u003C\u002Fspan> \n\u003Cspan>11\u003C\u002Fspan>     \u003Cspan>public\u003C\u002Fspan> \u003Cspan>static\u003C\u002Fspan> \u003Cspan>void\u003C\u002Fspan> main(String[] args) \u003Cspan>throws\u003C\u002Fspan>\u003Cspan> InterruptedException {\n\u003C\u002Fspan>\u003Cspan>12\u003C\u002Fspan>         Thread sleepTask = \u003Cspan>new\u003C\u002Fspan> Thread(() -&gt; { \u003Cspan>\u002F\u002F\u003C\u002Fspan>\u003Cspan>睡眠线程\u003C\u002Fspan>\n\u003Cspan>13\u003C\u002Fspan>             \u003Cspan>try\u003C\u002Fspan>\u003Cspan> {\n\u003C\u002Fspan>\u003Cspan>14\u003C\u002Fspan>                 Thread.sleep(2000L\u003Cspan>);\n\u003C\u002Fspan>\u003Cspan>15\u003C\u002Fspan>             } \u003Cspan>catch\u003C\u002Fspan>\u003Cspan> (InterruptedException e) {\n\u003C\u002Fspan>\u003Cspan>16\u003C\u002Fspan>                 \u003Cspan>\u002F\u002F\u003C\u002Fspan>\u003Cspan> do sth\u003C\u002Fspan>\n\u003Cspan>17\u003C\u002Fspan> \u003Cspan>            }\n\u003C\u002Fspan>\u003Cspan>18\u003C\u002Fspan>             time_out = \u003Cspan>true\u003C\u002Fspan>\u003Cspan>;\n\u003C\u002Fspan>\u003Cspan>19\u003C\u002Fspan> \u003Cspan>        });\n\u003C\u002Fspan>\u003Cspan>20\u003C\u002Fspan> \n\u003Cspan>21\u003C\u002Fspan> \n\u003Cspan>22\u003C\u002Fspan>         Thread t1 = \u003Cspan>new\u003C\u002Fspan> Thread(() -&gt;\u003Cspan> {\n\u003C\u002Fspan>\u003Cspan>23\u003C\u002Fspan>             System.out.println(\"t1 start ,state:\" +\u003Cspan> Thread.currentThread().isInterrupted());\n\u003C\u002Fspan>\u003Cspan>24\u003C\u002Fspan> \u003Cspan>            sleepTask.start();\n\u003C\u002Fspan>\u003Cspan>25\u003C\u002Fspan> \n\u003Cspan>26\u003C\u002Fspan>             \u003Cspan>while\u003C\u002Fspan> (!\u003Cspan>time_out) {\n\u003C\u002Fspan>\u003Cspan>27\u003C\u002Fspan>                 \u003Cspan>\u002F\u002F\u003C\u002Fspan>\u003Cspan>wait sleep task\u003C\u002Fspan>\n\u003Cspan>28\u003C\u002Fspan> \u003Cspan>            }\n\u003C\u002Fspan>\u003Cspan>29\u003C\u002Fspan>             System.out.println(\"t1 wait sleep task end,state:\" +\u003Cspan> Thread.currentThread().isInterrupted());\n\u003C\u002Fspan>\u003Cspan>30\u003C\u002Fspan>             \u003Cspan>synchronized\u003C\u002Fspan>\u003Cspan> (lock) {\n\u003C\u002Fspan>\u003Cspan>31\u003C\u002Fspan>                 System.out.println(\"t1 task  start\"\u003Cspan>);\n\u003C\u002Fspan>\u003Cspan>32\u003C\u002Fspan>                 \u003Cspan>try\u003C\u002Fspan>\u003Cspan> {\n\u003C\u002Fspan>\u003Cspan>33\u003C\u002Fspan>                     System.out.println(\"t1 task  do sth\"\u003Cspan>);\n\u003C\u002Fspan>\u003Cspan>34\u003C\u002Fspan> \u003Cspan>                    lock.wait();\n\u003C\u002Fspan>\u003Cspan>35\u003C\u002Fspan>                 } \u003Cspan>catch\u003C\u002Fspan>\u003Cspan> (InterruptedException e) {\n\u003C\u002Fspan>\u003Cspan>36\u003C\u002Fspan>                     System.out.println(\"t1 catch interrupt ex ,state: \" +\u003Cspan> Thread.currentThread().isInterrupted());\n\u003C\u002Fspan>\u003Cspan>37\u003C\u002Fspan> \u003Cspan>                }\n\u003C\u002Fspan>\u003Cspan>38\u003C\u002Fspan>                 System.out.println(\"task end \"\u003Cspan>);\n\u003C\u002Fspan>\u003Cspan>39\u003C\u002Fspan> \u003Cspan>            }\n\u003C\u002Fspan>\u003Cspan>40\u003C\u002Fspan> \u003Cspan>        });\n\u003C\u002Fspan>\u003Cspan>41\u003C\u002Fspan> \n\u003Cspan>42\u003C\u002Fspan> \u003Cspan>        t1.start();\n\u003C\u002Fspan>\u003Cspan>43\u003C\u002Fspan>         Thread.sleep(500L); \u003Cspan>\u002F\u002F\u003C\u002Fspan>\u003Cspan>等待t1拉起睡眠线程\u003C\u002Fspan>\n\u003Cspan>44\u003C\u002Fspan>         \u003Cspan>synchronized\u003C\u002Fspan>\u003Cspan> (lock) {\n\u003C\u002Fspan>\u003Cspan>45\u003C\u002Fspan>             System.out.println(\"before notify, t1 state-0 :\" +\u003Cspan> t1.isInterrupted());\n\u003C\u002Fspan>\u003Cspan>46\u003C\u002Fspan> \u003Cspan>            t1.interrupt();\n\u003C\u002Fspan>\u003Cspan>47\u003C\u002Fspan>             System.out.println(\"before notify, t1 state-1 :\" +\u003Cspan> t1.isInterrupted());\n\u003C\u002Fspan>\u003Cspan>48\u003C\u002Fspan> \u003Cspan>        }\n\u003C\u002Fspan>\u003Cspan>49\u003C\u002Fspan> \u003Cspan>        t1.join();\n\u003C\u002Fspan>\u003Cspan>50\u003C\u002Fspan> \u003Cspan>    }\n\u003C\u002Fspan>\u003Cspan>51\u003C\u002Fspan> \n\u003Cspan>52\u003C\u002Fspan> }\u003C\u002Fpre>\n\u003C\u002Fdiv>\n\u003Cp>\u003Cspan>大致逻辑是t1启动以后，先等待2s（通过等待另外一个线程修改完标记来做到），\u003C\u002Fspan>\u003Cbr>\u003Cspan>2s结束后t1才开始获取锁，在获取锁之前主线程先拿到锁并且打上中断标记，并且释放锁。(防盗连接：本文首发自http:\u002F\u002Fwww.cnblogs.com\u002Fjilodream\u002F )\u003C\u002Fspan>\u003Cbr>\u003Cspan>此时t1抢到锁，开始等待，此时由于之前已经被打上中断印记了，因此这里会直接抛出异常，结束任务。\u003C\u002Fspan>\u003C\u002Fp>\n\u003Cp>\u003Cspan>实际输出如下：\u003C\u002Fspan>\u003C\u002Fp>\n\u003Cdiv>\n \u003Cpre>Connected to the target VM, address: '127.0.0.1:52345', transport: 'socket'\u003Cspan>\nt1 start ,state:\u003C\u002Fspan>\u003Cspan>false\u003C\u002Fspan>\u003Cspan>\nbefore notify, t1 state\u003C\u002Fspan>-0 :\u003Cspan>false\u003C\u002Fspan>\u003Cspan>\nbefore notify, t1 state\u003C\u002Fspan>-1 :\u003Cspan>true\u003C\u002Fspan>\u003Cspan>\nt1 wait sleep task end,state:\u003C\u002Fspan>\u003Cspan>true\u003C\u002Fspan>\u003Cspan>\nt1 task  start\nt1 task  \u003C\u002Fspan>\u003Cspan>do\u003C\u002Fspan>\u003Cspan> sth\nt1 \u003C\u002Fspan>\u003Cspan>catch\u003C\u002Fspan> interrupt ex ,state: \u003Cspan>false\u003C\u002Fspan>\u003Cspan>\ntask end \nDisconnected from the target VM, address: \u003C\u002Fspan>'127.0.0.1:52345', transport: 'socket'\u003C\u002Fpre>\n\u003C\u002Fdiv>\n\u003Cp>\u003Cspan>但是这里又会有一个新的问题，如果线程t1内部逻辑很复杂，方法套方法，(防盗连接：本文首发自http:\u002F\u002Fwww.cnblogs.com\u002Fjilodream\u002F )出现了多个wait\u002Fsleep等阻塞动作怎么办，我们本意是希望终止线程，结果到处阻塞，我总不能在外边写一个while循环，不断的打断线程吧。\u003C\u002Fspan>\u003Cbr>\u003Cspan>大可不必，一般推荐的办法是，在catch到终止异常时，判断是否真的要终止，还是需要继续执行剩余代码（一般后端守护线程都需要继续再执行）。\u003C\u002Fspan>\u003Cbr>\u003Cspan>如果确定要终止，则在catch中直接调用Thread.currentThread().interrupt();直接自己给自己再打一个终止标记即可。\u003C\u002Fspan>\u003Cbr>\u003Cspan>这样即使后边再遇到check 或者 阻塞的地方，都会直接按照已中断继续的来处理。\u003C\u002Fspan>\u003Cbr>\u003Cspan>因此当有interrupt方法时，我们最好不要直接catch Exception 这样的大异常，使得维护阶段，忘记这里可能线程被中断，从而继续跑任务。\u003C\u002Fspan>\u003C\u002Fp>","之前的线程协作，讲的是通过wait和notify方法，多线程之间进行互相条件唤醒的办法。除此之外我们还需要进行中断操作。 等待和唤醒可参考前文https:\u002F\u002Fwww.cnblogs.com\u002Fjilodream\u002Fp\u002F22770136 设想这样一个场景：长工在给地主家干活，日出而作，日落而息。 思路很简单： 长工线程的伪代码 1 public void run (){ 2 while(true) { 3 if (time 早于 日出){ 4 \u002F\u002F继续休息 5 \u002F\u002Fcontinue; 6 } 7 if (time 早于 日落){ 8 \u002F\u002F继续工作 9 \u002F\u002Fcontinue; 10 } 11 \u002F\u002F时间属于日落 12 \u002F\u002F结束工作 13 \u002F\u002Freturn; 14 } 15 } 这就是最典型的通过标志位状态来中断线程的操作。除了开发人员之外，java本身也给线程类内置了一个中断状态的标记变量。 \u002F* Interrupt state of the thread - read\u002Fwritten directly by JVM *\u002F private volatile boolean interrupted; 注释的意思：这是线程的中断状态，读写都是由JVM直接操纵的。 线程可以通过这个标记，来check自身的状态。并且针对这个变量，jdk提供了几个核心方法，供开发人员直接使用： （1）中断这个线程，注意这是实例方法，所中断的线程也就是这个线程实例。 public void interrupt() （2）查看这个线程的中断标记，注意这是实例方法，所查看的线程标记变量也就是这个线程实例内置的中断标记。 public boolean isInterrupted() （3）查看线程的标记变量，注意这是一个静态方法，其所中断的线程也就是当前执行这个方法的线程currentThread。并且这个方法会清除标记，因此作者在命名时加了ed，表示是否过去被中断过。 public static boolean interrupted() 顺便依次看下源码： 1 public void interrupt() { 2 if (this != Thread.currentThread()) { 3 checkAccess(); 4 5 \u002F\u002F thread may be blocked in an I\u002FO operation 6 synchronized (blockerLock) { 7 Interruptible b = blocker; 8 if (b != null) { 9 interrupted = true; 10 interrupt0(); \u002F\u002F inform VM of interrupt 11 b.interrupt(this); 12 return; 13 } 14 } 15 } 16 interrupted = true; 17 \u002F\u002F inform VM of interrupt 18 interrupt0(); 19 } 20 21 private native void interrupt0(); 这个方法的大致逻辑是： 先判断中断当前方法的线程是不是就是当前线程， 如果不是话，check一下安全设置，检查是否可以跨线程中断其它线程，如果有问题这里会抛出一个运行时异常 SecurityException。如果安全检查也没问题，判断当前线程是否处于I\u002FO阻塞中。如果是的话就打断线程的I\u002FO阻塞（阻塞的地方通常会抛出IOException相关的异常。 另外通常只有nio 的阻塞才会中断，传统的io不会响应中断，read write 方法依然会阻塞住），设置标记位interrupted为true，表示被中断了。并且调用native 方法interrupt0，让jvm来处理中断的具体操作。 如果是当前线程，或者没有处于阻塞中的话，(防盗连接：本文首发自http:\u002F\u002Fwww.cnblogs.com\u002Fjilodream\u002F )这里直接设置标记位interrupted为true，并且调用native 方法interrupt0即可。 然后是获取中断状态，这里很简单，直接拿状态变量： 1 public boolean isInterrupted() { 2 return interrupted; 3 } 接着是获取并清理中断状态： 1 public static boolean interrupted() { 2 Thread t = currentThread(); 3 boolean interrupted = t.interrupted; 4 \u002F\u002F We may have been interrupted the moment after we read the field, 5 \u002F\u002F so only clear the field if we saw that it was set and will return 6 \u002F\u002F true; otherwise we could lose an interrupt. 7 if (interrupted) { 8 t.interrupted = false; 9 clearInterruptEvent(); 10 } 11 return interrupted; 12 } 13 14 private static native void clearInterruptEvent(); 核心逻辑是：取出当前线程t，以及当前线程的中断状态，如果当前线程的中断标记位true。那么就设置其为false，并且清理中断事件：告诉jvm，底层未处理的pending状态的中断信号清理掉。（后文红色字体会说到） 知道了这些api的作用，我们来看一个简单的例子： 1 public class InterruptStudy { 2 public static void main(String[] args) throws InterruptedException { 3 4 5 Thread t1 = new Thread(() -> { 6 while (!Thread.currentThread().isInterrupted()) { 7 System.out.println(\"do sth\"); 8 } 9 System.out.println(\"task end, interrupt state \" + Thread.currentThread().isInterrupted()); 10 return; 11 }); 12 13 t1.start(); 14 Thread.sleep(1000L); 15 t1.interrupt(); 16 t1.join(); 17 } 18 19 } 输出结果如下： Connected to the target VM, address: '127.0.0.1:53895', transport: 'socket' do sth do sth do sth do sth .... do sth do sth do sth do sth task end, interrupt state true Disconnected from the target VM, address: '127.0.0.1:53895', transport: 'socket' Process finished with exit code 0 这样子就可以通过中断标记，和线程进行交互从而中断线程了。 再改为使用静态方法： 1 public class InterruptStudy { 2 public static void main(String[] args) throws InterruptedException { 3 4 5 Thread t1 = new Thread(() -> { 6 while (!Thread.interrupted()) { 7 System.out.println(\"do sth\"); 8 } 9 System.out.println(\"task end, interrupt state \" + Thread.currentThread().isInterrupted()); 10 return; 11 }); 12 13 t1.start(); 14 Thread.sleep(10L); 15 t1.interrupt(); 16 t1.join(); 17 } 18 19 } 输出结果如下： Connected to the target VM, address: '127.0.0.1:60872', transport: 'socket' do sth do sth do sth .... do sth do sth do sth do sth do sth do sth do sth task end, interrupt state false Disconnected from the target VM, address: '127.0.0.1:60872', transport: 'socket' Process finished with exit code 0 发现中断以后，重置了中断标记，因此就返回false。 但是java的中断能力远不止此，如果仅仅是这样，那也太容易了。还记得上篇文章的wait么，以及更早之前的sleep，如果线程处于这种状态，(防盗连接：本文首发自http:\u002F\u002Fwww.cnblogs.com\u002Fjilodream\u002F )无法自行的check 中断状态怎么办。没关系jvm 会自行的帮你唤醒，抛出一个InterruptedException。 同时在抛出异常之前，jvm还会帮你做两件事：清理标记，重新尝试获取锁。 请看下边这个例子： 1 public class InterruptExStudy { 2 private static final Object lock = new Object(); 3 4 public static void main(String[] args) throws InterruptedException { 5 6 7 Thread t1 = new Thread(() -> { 8 synchronized (lock) { 9 System.out.println(\"task start\"); 10 try { 11 System.out.println(\"task do sth\"); 12 lock.wait(); 13 } catch (InterruptedException e) { 14 System.out.println(\"task catch interrupt ex ,state: \" + Thread.currentThread().isInterrupted()); 15 } 16 System.out.println(\"task end \"); 17 } 18 }); 19 20 t1.start(); 21 Thread.sleep(1000L); 22 synchronized (lock) { 23 System.out.println(\"before notify, t1 state-0 :\" + t1.isInterrupted()); 24 t1.interrupt(); 25 System.out.println(\"before notify, t1 state-1 :\" + t1.isInterrupted()); 26 } 27 t1.join(); 28 } 29 30 } 输出如下： Connected to the target VM, address: '127.0.0.1:51128', transport: 'socket' task start task do sth before notify, t1 state-0 :false before notify, t1 state-1 :true task catch interrupt ex ,state: false task end Disconnected from the target VM, address: '127.0.0.1:51128', transport: 'socket' Process finished with exit code 0 大致逻辑是这样的： 主线程启动t1线程，t1线程抢到锁，t1处于wait状态（同时释放锁）， 主线程抢到锁，主线程中断t1，t1需要再次抢到锁才能抛出中断异常， 当后续在抛出中断异常的时候，t1内部的中断标记也被重置了。 这样子即使线程处于join\u002Fwait\u002Fsleep等状态下，无法主动check状态，我们也可以通过抛出异常的形式，来中断线程了。同时根据前文中的jdk源码，也可以知道，即使在IO等待的状态下，也会被中断。而一开始说的api：interrupted()中的源码中，会调用clearInterruptEvent() 清理中断事件，其作用就是为了清理这些中断异常抛出等事件，保证虽然标记被恢复为false，对应的中断事件也都被清理干净了。 另外还需要注意的是，中断是一个累加状态，也就是说即使当前线程还没开始阻塞（join\u002Fwait\u002Fsleep\u002Fio阻塞等），一旦被标记为中断，即使后边再遇到阻塞场景，也一样被中断。来看这个例子： 1 import java.util.concurrent.TimeUnit; 2 3 \u002F** 4 * @discription 5 *\u002F 6 public class InterruptEx1Study { 7 private static final Object lock = new Object(); 8 9 static volatile boolean time_out = false; 10 11 public static void main(String[] args) throws InterruptedException { 12 Thread sleepTask = new Thread(() -> { \u002F\u002F睡眠线程 13 try { 14 Thread.sleep(2000L); 15 } catch (InterruptedException e) { 16 \u002F\u002F do sth 17 } 18 time_out = true; 19 }); 20 21 22 Thread t1 = new Thread(() -> { 23 System.out.println(\"t1 start ,state:\" + Thread.currentThread().isInterrupted()); 24 sleepTask.start(); 25 26 while (!time_out) { 27 \u002F\u002Fwait sleep task 28 } 29 System.out.println(\"t1 wait sleep task end,state:\" + Thread.currentThread().isInterrupted()); 30 synchronized (lock) { 31 System.out.println(\"t1 task start\"); 32 try { 33 System.out.println(\"t1 task do sth\"); 34 lock.wait(); 35 } catch (InterruptedException e) { 36 System.out.println(\"t1 catch interrupt ex ,state: \" + Thread.currentThread().isInterrupted()); 37 } 38 System.out.println(\"task end \"); 39 } 40 }); 41 42 t1.start(); 43 Thread.sleep(500L); \u002F\u002F等待t1拉起睡眠线程 44 synchronized (lock) { 45 System.out.println(\"before notify, t1 state-0 :\" + t1.isInterrupted()); 46 t1.interrupt(); 47 System.out.println(\"before notify, t1 state-1 :\" + t1.isInterrupted()); 48 } 49 t1.join(); 50 } 51 52 } 大致逻辑是t1启动以后，先等待2s（通过等待另外一个线程修改完标记来做到）， 2s结束后t1才开始获取锁，在获取锁之前主线程先拿到锁并且打上中断标记，并且释放锁。(防盗连接：本文首发自http:\u002F\u002Fwww.cnblogs.com\u002Fjilodream\u002F ) 此时t1抢到锁，开始等待，此时由于之前已经被打上中断印记了，因此这里会直接抛出异常，结束任务。 实际输出如下： Connected to the target VM, address: '127.0.0.1:52345', transport: 'socket' t1 start ,state:false before notify, t1 state-0 :false before notify, t1 state-1 :true t1 wait sleep task end,state:true t1 task start t1 task do sth t1 catch interrupt ex ,state: false task end Disconnected from the target VM, address: '127.0.0.1:52345', transport: 'socket' 但是这里又会有一个新的问题，如果线程t1内部逻辑很复杂，方法套方法，(防盗连接：本文首发自http:\u002F\u002Fwww.cnblogs.com\u002Fjilodream\u002F )出现了多个wait\u002Fsleep等阻塞动作怎么办，我们本意是希望终止线程，结果到处阻塞，我总不能在外边写一个while循环，不断的打断线程吧。 大可不必，一般推荐的办法是，在catch到终止异常时，判断是否真的要终止，还是需要继续执行剩余代码（一般后端守护线程都需要继续再执行）。 如果确定要终止，则在catch中直接调用Thread.currentThread().interrupt();直接自己给自己再打一个终止标记即可。 这样即使后边再遇到check 或者 阻塞的地方，都会直接按照已中断继续的来处理。 因此当有interrupt方法时，我们最好不要直接catch Exception 这样的大异常，使得维护阶段，忘记这里可能线程被中断，从而继续跑任务。",7044,{"id":6,"kind":7,"title":11,"summary":13,"image":14,"href":15,"meta":36,"badge":10,"author":12,"stats":-1,"accent":37,"coverRatio":38,"tags":39},"2026 · 软件开发","#2563eb","16 \u002F 10",[18],{"targetType":8,"targetId":9,"likedByMe":41,"likeCount":42,"commentCount":42,"contentLikeCount":42,"contentCommentCount":42,"sourceLikeCount":42,"sourceCommentCount":42},false,0,[44,51,57,64,71,78,84,90],{"id":45,"kind":7,"title":46,"summary":47,"image":48,"href":49,"meta":36,"badge":10,"author":12,"stats":-1,"accent":37,"coverRatio":38,"tags":50},"NEWS_ARTICLE:927","HelloCrab - 短视频开源爬虫，仅供学习参考","HelloCrab 基于 Avalonia、Playwright、AI与 FFmpeg 的跨平台桌面采集器，支持9大平台，以及 Android、iOS、Browser 远程控制端。 Made By ChatGPT &amp; Vincent with ❤ 平台 是否接入 哔哩哔哩 ✅ 抖音 ✅ 快手","https:\u002F\u002Fwww.cnblogs.com\u002Fhupo376787\u002Fp\u002FScreenshot\u002FWindows.jpg","\u002Fnews\u002F927",[18],{"id":52,"kind":7,"title":53,"summary":54,"image":14,"href":55,"meta":36,"badge":10,"author":12,"stats":-1,"accent":37,"coverRatio":38,"tags":56},"NEWS_ARTICLE:929",".NET 异常处理的\"暗门\"：代码里写满 catch，你依然能抓住它——从一个 AI Agent 运行时的源码说起","一个健壮的系统，必然到处都是有意的 catch；异常被消化不等于问题不存在。 观测与韧性，是一个硬币的两面——降级逻辑保证系统不崩，FirstChance 保证你能看见它为什么降级。","\u002Fnews\u002F929",[18],{"id":58,"kind":7,"title":59,"summary":60,"image":61,"href":62,"meta":36,"badge":10,"author":12,"stats":-1,"accent":37,"coverRatio":38,"tags":63},"NEWS_ARTICLE:933","写给 C++ 工程师的 OpenClaw.NET 上手指南：用你熟悉的 C++ 思维，跑起一个生产级 AI Agent","它像一个「基于 boost.asio + REST 端点的常驻服务」——对外是 HTTP \u002F WebSocket \u002F 各 IM 的 webhook，对内跑着一个能调工具、读写记忆、跨渠道对话的 AI Agent——只不过协程不用你手写 promise_type，内存不用你管 new\u002Fdelete。","https:\u002F\u002Fimg2024.cnblogs.com\u002Fblog\u002F510\u002F202609\u002F510-20260905070811424-1837144874.jpg","\u002Fnews\u002F933",[18],{"id":65,"kind":7,"title":66,"summary":67,"image":68,"href":69,"meta":36,"badge":10,"author":12,"stats":-1,"accent":37,"coverRatio":38,"tags":70},"NEWS_ARTICLE:952",".NET 11 RC1 发布：拿到\"准生证\"，生产环境可以上了！","从 Preview 1 到 RC1，.NET 11 的拼图基本完整了：Runtime Async 反超状态机模型、CoreCLR 登陆 WebAssembly、CLI 全面 NativeAOT 化（dotnet tool list 快 5.5 倍）、C# 15 补齐 union 和 labeled","https:\u002F\u002Fimg2024.cnblogs.com\u002Fblog\u002F510\u002F202609\u002F510-20260909153805786-850851443.png","\u002Fnews\u002F952",[18],{"id":72,"kind":7,"title":73,"summary":74,"image":75,"href":76,"meta":36,"badge":10,"author":12,"stats":-1,"accent":37,"coverRatio":38,"tags":77},"NEWS_ARTICLE:961","从零搭建ELK日志采集系统：Filebeat + Logstash + ES + Kibana 保姆级教程","轻量级架构，一份配置全搞定 一、前言 你是不是还在用 tail -f 和 grep 在多台服务器上翻日志？系统一出问题，就要登录三五台机器，来回切换窗口，定位一个Bug耗时半小时。 ELK 是业界最成熟的日志集中管理方案。本文将带你用 Docker Compose 一键部署 Filebeat + L","https:\u002F\u002Fimg2024.cnblogs.com\u002Fblog\u002F1465907\u002F202609\u002F1465907-20260909162947190-195439361.png","\u002Fnews\u002F961",[18],{"id":79,"kind":7,"title":80,"summary":81,"image":61,"href":82,"meta":36,"badge":10,"author":12,"stats":-1,"accent":37,"coverRatio":38,"tags":83},"NEWS_ARTICLE:980","写给 Rust 工程师的 OpenClaw.NET 上手指南：用你熟悉的 Rust 思维，跑起一个生产级 AI Agent","它像一个「axum 应用」——对外是 HTTP \u002F WebSocket \u002F 各 IM 的 webhook，对内跑着一个能调工具、读写记忆、跨渠道对话的 AI Agent——只不过 async 不用选 runtime，也不用和借用检查器格斗。","\u002Fnews\u002F980",[18],{"id":85,"kind":7,"title":86,"summary":87,"image":14,"href":88,"meta":36,"badge":10,"author":12,"stats":-1,"accent":37,"coverRatio":38,"tags":89},"NEWS_ARTICLE:999","Vane.Dispatch 1.0.0 发布：一个与容器、传输层零耦合的 .NET 服务分发引擎","Vane.Dispatch 1.0.0 发布：一个与容器、传输层零耦合的 .NET 服务分发引擎 它的前身，可以追溯到一个叫 Ndf 的项目。从那时算起，已经过去了十多年。今天，它以 Vane.Dispatch 1.0.0 的名字正式发布。 序：十年磨一剑 做后端的人大抵都绕不开同一个问题：如何把&","\u002Fnews\u002F999",[18],{"id":91,"kind":7,"title":92,"summary":93,"image":94,"href":95,"meta":36,"badge":10,"author":12,"stats":-1,"accent":37,"coverRatio":38,"tags":96},"NEWS_ARTICLE:1002","从对标 Java 到对标 Go：Native AOT 的\"无痛化\"之路，走到哪一站了？","从 .NET 7 的 demo 到 .NET 12 的无痛化，这条路要走五年。慢吗？慢。但对比一下：Java 的 GraalVM Native Image 折腾了更久，至今 Spring 生态的 AOT 体验仍在打补丁；Go 则是天生就站在终点线上——.NET 是在背着二十年的反射遗产追赶一个轻装上","https:\u002F\u002Fimg2024.cnblogs.com\u002Fblog\u002F510\u002F202609\u002F510-20260907133134585-1733425989.png","\u002Fnews\u002F1002",[18]]