停止线程 https://www.e-learn.cn/tag/tingzhixiancheng zh-hans 线程的正确停止 https://www.e-learn.cn/topic/3461007 <span>线程的正确停止</span> <span><span lang="" about="/user/225" typeof="schema:Person" property="schema:name" datatype="">二次信任</span></span> <span>2020-03-02 17:04:17</span> <div class="field field--name-body field--type-text-with-summary field--label-hidden field--item"> <span id="OSC_h3_1"></span> <h3><strong>线程停止的几种方式:</strong><br /></h3> <ol><li><p>线程运行完成,正常停止</p></li> <li><p>执行stop方法,暴力停止,现已弃用</p></li> <li><p>执行interrupt方法,现在使用。</p></li> </ol><span id="OSC_h3_2"></span> <h3><strong>stop方法为什么被弃用?</strong></h3> <p><span style="font-family: 微软雅黑, 'Microsoft YaHei';">使用stop方法终止线程会<span style="font-family: 微软雅黑, 'Microsoft YaHei'; background-color: rgb(192, 80, 77);">释放掉此线程锁定的所有的监视器</span>,如果线程修改了锁定对象的内容在还没有被正常处理之前线程被终止了。将会造成数据不一致的后果。</span></p> <p><span style="font-family: 微软雅黑, 'Microsoft YaHei'; font-size: 14px;">例如银行取款的例子:在线程A中进行取款操作,取款操作是一个同步方法,其中共有三个步骤:输入密码,取钱,修改余额。当用户a在输入密码,取钱之后,线程A.stop();线程终止。被修改的余额还没有写回数据库,从而造成数据混乱。</span></p> <p>举例说明:首先定义一个操作类Login</p> <pre class="brush:java;toolbar: true; auto-links: false;"><code>package com.feng.example; public class Login { private String username="a"; private String password="aa"; synchronized public String getUsername() { return username; } public void setUsername(String username) {  this.username = username; } synchronized public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } synchronized public void login(String username, String passwd) { this.username = username; try { Thread.sleep(20000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } this.password = passwd; } }</code></pre> <p>定义线程类,调用login方法</p> <pre class="brush:java;toolbar: true; auto-links: false;"><code>package com.feng.example; public class LoginThread extends Thread{ private Login login; public LoginThread(Login login) { this.login = login; } @Override public void run() { // TODO Auto-generated method stub super.run(); login.login("b", "bb"); } }</code></pre> <p>测试类:</p> <pre class="brush:java;toolbar: true; auto-links: false;"><code>package com.feng.example; public class LoginTest { /**  * @param args  */ public static void main(String[] args) { // TODO Auto-generated method stub Login login = new Login(); Thread thread = new LoginThread(login); thread.start(); try { Thread.sleep(2000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } thread.stop();  //强制终止线程 System.out.println(login.getUsername()+"========"+login.getPassword()); } }</code></pre> <p>分析:login对象的username初值为a,password初值为aa,线程thread调用login("b","bb");方法,将username的值修改为b,此时线程休眠20秒,在线程thread start之后2秒调用thread.stop,线程终止。释放掉对login对象的锁。只有释放掉对login的锁才能调用同步方法getUsername和getPassword.</p> <p>程序运行结果:</p> <pre class="brush:java;toolbar: true; auto-links: false;"><code>b========aa</code></pre> <span id="OSC_h3_3"></span> <h3><strong>正确的线程停止方式</strong></h3> <p>使用interrupt方法,此方法只是给线程一个标记,表示此线程要被终止(只是一个标记,没有做出任何终止线程的行为),通过配合使用interrupted方法或者isInterrupted方法来检测线程是否要停止,如果检测到要停止,则先进行完相应的操作之后使用break或者return正常执行完(在有循环体的情况下)或者使用抛出异常的方式终止线程(推荐使用)</p> <p>通过上述内容可以看出正确停止线程的思想就是:<span style="background-color: rgb(192, 80, 77);">利用某种手段使线程正常停止或者抛出异常的方式终止线程</span></p> <span id="OSC_h4_4"></span> <h4>1.介绍interrupted和isInterrupted方法</h4> <p><span style="background-color: rgb(192, 80, 77);">interrupted方法 静态方法  检测当前线程(执行这个方法的线程)是否被中断(判断有没有中断标记)</span></p> <p><span style="background-color: rgb(192, 80, 77);">isInterrupted方法  检测调用者是否被中断</span></p> <p>测试interrupted方法的使用,创建MyThread线程类</p> <pre class="brush:java;toolbar: true; auto-links: false;"><code>package com.feng.example; public class MyThread extends Thread { @Override public void run() { int i = 0; while(true) { i++; } } }</code></pre> <p>测试类:</p> <pre class="brush:java;toolbar: true; auto-links: false;"><code>package com.feng.example; public class ThreadTest { /**  * @param args  */ public static void main(String[] args) { Thread thread = new MyThread(); thread.start(); thread.interrupt(); System.out.println(thread.interrupted()+"=========="+Thread.interrupted()); //main函数执行的这段代码,所以当前线程是main System.out.println(thread.interrupted()+"=========="+Thread.interrupted()); } }</code></pre> <p>分析:在main函数中启动thread,然后使用thread.interrupt()终止线程(其实就是打一个终止标记),而输出语句中使用的是interrupted方法,此方法检测的是当前线程,即执行这句话的线程也就是main,因为终止的是thread而不是main,所以打印出来的结果都是false。<br /></p> <p>这里因为interrupted是静态方法,使用thread.interrupted() 和Thread.interrupted()是一样的。</p> <p>测试结果:</p> <pre class="brush:java;toolbar: true; auto-links: false;"><code>false==========false false==========false</code></pre> <p>修改上面的测试程序,使主线程终止,修改程序如下:</p> <pre class="brush:java;toolbar: true; auto-links: false;"><code>package com.feng.example; public class ThreadTest { /**  * @param args  */ public static void main(String[] args) { Thread thread = new MyThread(); thread.start(); Thread.currentThread().interrupt(); System.out.println(thread.interrupted()+"=========="+Thread.interrupted()); //main函数执行的这段代码,所以当前线程是main System.out.println(thread.interrupted()+"=========="+Thread.interrupted()); } }</code></pre> <p>分析:现在终止的是主线程,因此在输出的时候调用thread.interrupted()输出true。因为<span style="background-color: rgb(192, 80, 77);">interrupted方法具有清除中断标记的功能</span>,因此再次调用interrupted()方法时,输出false</p> <p>测试如果如下:</p> <pre class="brush:java;toolbar: true; auto-links: false;"><code>true==========false false==========false</code></pre> <p>修改测试程序查看isInterrupted方法的使用:</p> <pre class="brush:java;toolbar: true; auto-links: false;"><code>package com.feng.example; public class ThreadTest { /**  * @param args  */ public static void main(String[] args) { Thread thread = new MyThread(); thread.start(); thread.interrupt(); System.out.println(thread.isInterrupted()+"=========="+thread.isInterrupted()); //main函数执行的这段代码,所以当前线程是main } }</code></pre> <p>分析:<span style="background-color: rgb(192, 80, 77);">isInterrupted方法不具有清除中断标记的作用</span>,因此两次输出都为true</p> <p>运行结果:</p> <pre class="brush:java;toolbar: true; auto-links: false;"><code>true==========true</code></pre> <span id="OSC_h4_5"></span> <h4>2.使用break跳出循环,是线程运行完毕</h4> <p>修改线程类<br /></p> <pre class="brush:java;toolbar: true; auto-links: false;"><code>package com.feng.example; public class MyThread extends Thread { @Override public void run() { int i = 0; while(true) { if(this.interrupted())  //这里换做this.isInterrupted()也可以 { //做相应的处理 System.out.println("线程正常终止"); break; } i++; } } }</code></pre> <p>测试类代码:</p> <pre class="brush:java;toolbar: true; auto-links: false;"><code>package com.feng.example; public class ThreadTest { /**  * @param args  */ public static void main(String[] args) { Thread thread = new MyThread(); thread.start(); thread.interrupt(); } }</code></pre> <p>当在while(true)循环中判断如果又中断则跳出循环,从此处也可以看到thread.interrupt();只是做一个中断标记,当线程检测到这个中断标记后,可以做一些必要的操作后跳出循环,正常运行完线程。</p> <p>运行结果:</p> <pre class="brush:java;toolbar: true; auto-links: false;"><code>线程正常终止</code></pre> <span id="OSC_h4_6"></span> <h4>3.使用return退出线程<br /></h4> <p>如果上述的线程中有多个循环,break就只能跳出一个循环,从而进入另一个循环中,还是终止不了线程。我们可以使用return来退出循环。</p> <p>先看问题:修改上述的线程类</p> <pre class="brush:java;toolbar: true; auto-links: false;"><code>package com.feng.example; public class MyThread extends Thread { @Override public void run() { for(int i=0; i&lt;1000; i++) { if(this.isInterrupted()) { //做相应的处理 System.out.println("线程正常终止"); break; } i++; } System.out.println("线程还可以运行,如果我是一个循环又会进入一个循环"); } }</code></pre> <p>查看执行结果:</p> <pre class="brush:java;toolbar: true; auto-links: false;"><code>线程正常终止 线程还可以运行,如果我是一个循环又会进入一个循环</code></pre> <p>说明使用break的话,循环外的语句还是会被执行的,不想让循环外的语句执行就换为return</p> <p>修改线程类代码:</p> <pre class="brush:java;toolbar: true; auto-links: false;"><code>package com.feng.example; public class MyThread extends Thread { @Override public void run() { for(int i=0; i&lt;1000; i++) { if(this.isInterrupted()) { //做相应的处理 System.out.println("线程正常终止"); return; } i++; } System.out.println("线程还可以运行,如果我是一个循环又会进入一个循环"); } }</code></pre> <p>运行结果如下:</p> <pre class="brush:java;toolbar: true; auto-links: false;"><code>线程正常终止</code></pre> <p>return一般会对代码造成污染,因此我们还是建议使用抛出异常的方法,来终止线程</p> <span id="OSC_h4_7"></span> <h4>4.抛出异常,终止线程</h4> <p>修改线程类<br /></p> <pre class="brush:java;toolbar: true; auto-links: false;"><code>package com.feng.example; public class MyThread extends Thread { @Override public void run() { try { for (int i = 0; i &lt; 1000; i++) { if (this.isInterrupted()) { // 做相应的处理 System.out.println("线程正常终止"); throw new InterruptedException(); } i++; } System.out.println("线程还可以运行,如果我是一个循环又会进入一个循环"); } catch (InterruptedException e) { System.out.println("进入Catch"); e.printStackTrace(); } } }</code></pre> <p>运行结果:</p> <pre class="brush:java;toolbar: true; auto-links: false;"><code>线程正常终止 进入Catch java.lang.InterruptedException at com.feng.example.MyThread.run(MyThread.java:13)</code></pre> <span id="OSC_h3_8"></span> <h3><strong>interrupt睡眠中的线程</strong></h3> <span id="OSC_h4_9"></span> <h4>1.线程先进入sleep,然后再interrupt</h4> <p>定义线程类:<br /></p> <pre class="brush:java;toolbar: true; auto-links: false;"><code>package com.feng.example; public class MyThread extends Thread { @Override public void run() { try { System.out.println("线程执行"); Thread.sleep(10000); System.out.println("线程执行完成"); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); }  //休眠10秒 } }</code></pre> <p>测试类:</p> <pre class="brush:java;toolbar: true; auto-links: false;"><code>package com.feng.example; public class ThreadTest { /**  * @param args  */ public static void main(String[] args) { Thread thread = new MyThread(); thread.start(); try { Thread.sleep(2000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } thread.interrupt(); } }</code></pre> <p>终止sleep中的线程,会抛出interruptedException</p> <p>运行结果:</p> <pre class="brush:java;toolbar: true; auto-links: false;"><code>线程执行 java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at com.feng.example.MyThread.run(MyThread.java:10)</code></pre> <span id="OSC_h4_10"></span> <h4>2.先interrupted,线程再进入sleep</h4> <p>修改线程类:</p> <pre class="brush:java;toolbar: true; auto-links: false;"><code>package com.feng.example; public class MyThread extends Thread { @Override public void run() { try { //消耗一下时间, for(int i=0; i&lt;10000; i++) { System.out.println(i); } System.out.println("线程执行"); Thread.sleep(10000); System.out.println("线程执行完成"); } catch (InterruptedException e) { // TODO Auto-generated catch block System.out.println("进入Catch"); e.printStackTrace(); }  //休眠10秒 } }</code></pre> <p>测试类:</p> <pre class="brush:java;toolbar: true; auto-links: false;"><code>package com.feng.example; public class ThreadTest { /**  * @param args  */ public static void main(String[] args) { Thread thread = new MyThread(); thread.start(); thread.interrupt(); System.out.println("end"); } }</code></pre> <p>运行结果:</p> <pre class="brush:java;toolbar: true; auto-links: false;"><code>9997 9998 java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at com.feng.example.MyThread.run(MyThread.java:15) 9999 线程执行 进入Catch</code></pre> <p>由此可见:先sleep再interrupt会直接抛出异常,如果先interrupt,再进入sleep,在sleep时才会抛出异常</p> <div class="alert alert-success" role="alert"><p>来源:<code>oschina</code></p><p>链接:<code>https://my.oschina.net/u/2309504/blog/538873</code></p></div></div> <div class="field field--name-field-tags field--type-entity-reference field--label-above"> <div class="field--label">标签</div> <div class="field--items"> <div class="field--item"><a href="/tag/thread" hreflang="zh-hans">thread</a></div> <div class="field--item"><a href="/tag/stop" hreflang="zh-hans">stop</a></div> <div class="field--item"><a href="/tag/interrupt" hreflang="zh-hans">interrupt</a></div> <div class="field--item"><a href="/tag/interrupted" hreflang="zh-hans">interrupted</a></div> <div class="field--item"><a href="/tag/isinterrupted" hreflang="zh-hans">isInterrupted</a></div> <div class="field--item"><a href="/tag/tingzhixiancheng" hreflang="zh-hans">停止线程</a></div> </div> </div> Mon, 02 Mar 2020 09:04:17 +0000 二次信任 3461007 at https://www.e-learn.cn