关于Java中停止线程执行的方法总结

如何停止java的线程一直是开发多线程程序的一个非常头痛的问题,本文我们就来讨论一下关于Java中停止线程执行的方法,我们应该如何正确停止java中的线程。

Java中停止线程执行的方法

一、暂停或停止线程的理论

在Java编程中,要暂停或停止当前正在运行的线程,有几种方法。对于把线程转入睡眠Sleep状态,使用Thread.sleep()是最正确的方式。或许有人会问,为什么不使用等待wait()或通知notify()?要知道,使用等待或通知都不是很好的方式。
线程可以使用等待wait()实现被阻塞,这属于条件等待的方式,当条件满足后,又会从阻塞转为等待状态。尽管可以在等待wait()条件那里放一个超时设置,但等待wait()的设计目的不是这样的,等待wait()在设计上是用于Java线程间的通信。
而使用睡眠sleep()方式,可以让线程从当前开始睡眠指定的时间。注意不要使用睡眠sleep()方式去代替等待wait()或通知notify(),反之亦然。
Java线程通信的例子: http://javarevisited.blogspot.sg/2013/12/inter-thread-communication-in-java-wait-notify-example.html
等待wait()或通知notify()不应该用于暂停线程,还有一个原因,等待wait()或通知notify()需要一个锁。只能从一个同步的方法或同步的代码块去调用它们,获取锁和释放锁的开销是比较大的。而且,只是暂停线程的话,无需引入锁机制。
sleep()与wait()还有一点不同,sleep()会把当前的线程转入等待状态,它不会释放它持有的任何锁,而wait()使得线程转入阻塞状态,会释放掉自己持有的锁。
总之,Java多线程编程并不简单,即使是简单的任务,如创建线程、停止线程或暂停线程,都需要认真掌握Java API。

二、暂停或停止线程的实战

下面的例子中,要暂停线程,可以使用Thread.sleep()或TimeUnit.sleep()方法。例子中,有两个线程,主线程由JVM启动,它执行main()方法。第二个线程叫T1,它由主线程创建,用于循环运行游戏。我们传递的Runnable任务是一个无限循环,会一直运行直到我们停止它。注意看,我使用了volatile关键字。
主线程首先启动T1线程,再使用stop()方法停止线程。
在这个例子中,我们有两种方法停止线程的运行,使用Thread.sleep()方法或者是使用TimeUnit.sleep()方法。TimeUnit类既可以指定秒即TimeUnit.SECONDS,又可以指定毫秒即TimeUnit.MILLISECONDS。总的来说,使用TimeUnit的sleep()方法,使得代码更为易读。

点击(此处)折叠或打开

import static java.lang.Thread.currentThread;
import java.util.concurrent.TimeUnit;
public class ThreadPauseDemo{
    public static void main(String args[]) throws InterruptedException {
        Game game = new Game();
        Thread t1 = new Thread(game, “T1”);
        t1.start();
        // 现在停止Game线程
        System.out.println(currentThread().getName() + ” is stopping game thread”);
        game.stop();
        // 查看Game线程停止的状态
        TimeUnit.MILLISECONDS.sleep(200);
        System.out.println(currentThread().getName() + ” is finished now”);
    }
}

点击(此处)折叠或打开

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class  Game  implements  Runnable{      private  volatile  boolean  isStopped =  false ;      public  void  run(){          while (!isStopped){              System.out.println( "Game thread is running......" );              System.out.println( "Game thread is now going to pause" );              try {                  Thread.sleep( 200 );              } <a href= "/tags.php/catch/"  target= "_blank" > catch </a>(InterruptedException e){                  e.printStackTrace();              }              System.out.println( "Game thread is now resumed......" );          }          System.out.println( "Game thread is stopped......" );      }      public  void  stop(){          isStopped =  true ;      } }

程序输出如下:
Game thread is running……
main is stopping game thread
Game thread is now going to pause
Game thread is now resumed……
Game thread is stopped……
main is finished now

注:
volatile关键字:当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取这个值时,它会去主内存中读取新值。volatile关键字保证了可见性。普通的共享变量不能保证可见性,因为普通共享变量被修改之后,什么时候被写入主存是不确定的,当其他线程去读取时,此时内存中可能还是原来的旧值,因此无法保证可见性。
通过synchronized和Lock也能够保证可见性,synchronized和Lock能保证同一时刻只有一个线程获取锁然后执行同步代码,并且在释放锁之前会将对变量的修改刷新到主存当中。因此可以保证可见性。
一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么它就具备了两层语义:
1. 保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
2. 禁止进行指令重排序。
volatile关键字禁止指令重排序有两层意思:
1. 当程序执行到volatile变量的读操作或者写操作时,在其前面的操作的更改肯定全部已经进行,且结果已经对后面的操作可见;在其后面的操作肯定还没有进行。
2. 在进行指令优化时,不能将在对volatile变量访问的语句放在其后面执行,也不能把volatile变量后面的语句放到其前面执行。
volatile一般情况下不能代替sychronized,因为volatile不能保证操作的原子性,即使只是i++,实际上也是由多个原子操作组成:read i; inc; write i,假如多个线程同时执行i++,volatile只能保证他们操作的i是同一块内存,但依然可能出现写入脏数据的情况。

三、sleep()方法总结

Thread.sleep()方法可以让线程暂停或停止,它还有一些细节需要注意:
1. Thread.sleep()方法是一个静态方法,它总是可以让当前的线程转入睡眠。
2. 可以调用interrupt()方法把当前睡眠的线程唤醒。
3. sleep()方法不能保证线程能精准地在指定那一毫秒内转入睡眠,它的精度取决于系统的计时器。
4. 它不会释放它所获得的锁。

如何停止java线程

如何停止java的线程一直是一个困恼我们开发多线程程序的一个问题。这个问题最终在Java5的java.util.concurrent中得到了回答:使用interrupt(),让线程在run方法中停止。
简介

在Java的多线程编程中,java.lang.Thread类型包含了一些列的方法start(), stop(), stop(Throwable) and suspend(), destroy() and resume()。通过这些方法,我们可以对线程进行方便的操作,但是这些方法中,只有start()方法得到了保留。

在Sun公司的一篇文章《Why are Thread.stop, Thread.suspend and Thread.resume Deprecated? 》中详细讲解了舍弃这些方法的原因。那么,我们究竟应该如何停止线程呢?

建议使用的方法

在《Why are Thread.stop, Thread.suspend and Thread.resume Deprecated? 》中,建议使用如下的方法来停止线程:

1 2 3 4 5 6 7 8 9 10 11 12 13 14      private  volatile  Thread blinker;      public  void  stop() {          blinker =  null ;      }      public  void  run() {          Thread thisThread = Thread.currentThread();          while  (blinker == thisThread) {              try  {                  thisThread.sleep(interval);              catch  (InterruptedException e){              }              repaint();          }      }

关于使用volatile关键字的原因,请查看http://java.sun.com/docs/books/jls/second_edition/html/classes.doc.html#36930。
当线程处于非运行(Run)状态

当线程处于下面的状况时,属于非运行状态:

    当sleep方法被调用。

    当wait方法被调用。

    当被I/O阻塞,可能是文件或者网络等等。

当线程处于上述的状态时,使用前面介绍的方法就不可用了。这个时候,我们可以使用interrupt()来打破阻塞的情况,如:

1 2 3 4 5 6 7 public  void  stop() {          Thread tmpBlinker = blinker;          blinker =  null ;          if  (tmpBlinker !=  null ) {             tmpBlinker.interrupt();          }      }

当interrupt()被调用的时候,InterruptedException将被抛出,所以你可以再run方法中捕获这个异常,让线程安全退出:

1 2 3 4 5 6 try  {     ....     wait(); catch  (InterruptedException iex) {     throw  new  RuntimeException( "Interrupted" ,iex); }

阻塞的I/O

当线程被I/O阻塞的时候,调用interrupt()的情况是依赖与实际运行的平台的。在Solaris和Linux平台上将会抛出InterruptedIOException的异常,但是Windows上面不会有这种异常。所以,我们处理这种问题不能依靠于平台的实现。如:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 package  com.cnblogs.gpcuster import  java.net.*; import  java.io.*; public  abstract  class  InterruptibleReader  extends  Thread {      private  Object lock =  new  Object( );      private  InputStream is;      private  boolean  done;      private  int  buflen;      protected  void  processData( byte [] b,  int  n) { }      class  ReaderClass  extends  Thread {          public  void  run( ) {              byte [] b =  new  byte [buflen];              while  (!done) {                  try  {                      int  n = is.read(b,  0 , buflen);                      processData(b, n);                  catch  (IOException ioe) {                      done =  true ;                  }              }              synchronized (lock) {                  lock.notify( );              }          }      }      public  InterruptibleReader(InputStream is) {          this (is,  512 );      }      public  InterruptibleReader(InputStream is,  int  len) {          this .is = is;          buflen = len;      }      public  void  run( ) {          ReaderClass rc =  new  ReaderClass( );          synchronized (lock) {              rc.start( );              while  (!done) {                  try  {                      lock.wait( );                  catch  (InterruptedException ie) {                      done =  true ;                      rc.interrupt( );                      try  {                          is.close( );                      catch  (IOException ioe) {}                  }              }          }      } }

另外,我们也可以使用InterruptibleChannel接口。 实现了InterruptibleChannel接口的类可以在阻塞的时候抛出ClosedByInterruptException。如:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 package  com.cnblogs.gpcuster import  java.io.BufferedReader; import  java.io.FileDescriptor; import  java.io.FileInputStream; import  java.io.InputStream; import  java.io.InputStreamReader; import  java.nio.channels.Channels; public  class  InterruptInput {         static  BufferedReader in =  new  BufferedReader(              new  InputStreamReader(              Channels.newInputStream(              ( new  FileInputStream(FileDescriptor.in)).getChannel())));            public  static  void  main(String args[]) {          try  {              System.out.println( "Enter lines of input (user ctrl+Z Enter to terminate):" );              System.out.println( "(Input thread will be interrupted in 10 sec.)" );              // interrupt input in 10 sec              ( new  TimeOut()).start();              String line =  null ;              while  ((line = in.readLine()) !=  null ) {                  System.out.println( "Read line:'" +line+ "'" );              }          catch  (Exception ex) {              System.out.println(ex.toString());  // printStackTrace();          }      }            public  static  class  TimeOut  extends  Thread {          int  sleepTime =  10000 ;          Thread threadToInterrupt =  null ;              public  TimeOut() {              // interrupt thread that creates this TimeOut.              threadToInterrupt = Thread.currentThread();              setDaemon( true );          }                    public  void  run() {              try  {                  sleep( 10000 );  // wait 10 sec              catch (InterruptedException ex) { /*ignore*/ }              threadToInterrupt.interrupt();          }      } }

这里还需要注意一点,当线程处于写文件的状态时,调用interrupt()不会中断线程。

如何正确停止java中的线程 

为什么不能使用Thread.stop()方法?

从SUN的官方文档可以得知,调用Thread.stop()方法是不安全的,这是因为当调用Thread.stop()方法时,会发生下面两件事:

1. 即刻抛出ThreadDeath异常,在线程的run()方法内,任何一点都有可能抛出ThreadDeath Error,包括在catch或finally语句中。
2. 释放该线程所持有的所有的锁

当线程抛出ThreadDeath异常时,会导致该线程的run()方法突然返回来达到停止该线程的目的。ThreadDetath异常可以在该线程run()方法的任意一个执行点抛出。但是,线程的stop()方法一经调用线程的run()方法就会即刻返回吗?

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 public  static  void  main(String[] args) {             try  {                 Thread t =  new  Thread() {                     public  synchronized  void  run() {                         try  {                             long  start=System.currentTimeMillis();                             for  ( int  i =  0 ; i <  100000 ; i++)                                 System.out.println( "runing.."  + i);                             System.out.println((System.currentTimeMillis()-start)/ 1000 );                         catch  (Throwable ex) {                             System.out.println( "Caught in run: "  + ex);                             ex.printStackTrace();                         }                     }                 };                 t.start();                 // Give t time to get going...                 Thread.sleep( 100 );                 t.stop();  // EXPECT COMPILER WARNING             catch  (Throwable t) {                 System.out.println( "Caught in main: "  + t);                 t.printStackTrace();             }             }   public  static  void  main(String[] args) {            try  {                Thread t =  new  Thread() {                    public  synchronized  void  run() {                        try  {                            long  start=System.currentTimeMillis();                            for  ( int  i =  0 ; i <  100000 ; i++)                                System.out.println( "runing.."  + i);                            System.out.println((System.currentTimeMillis()-start)/ 1000 );                        catch  (Throwable ex) {                            System.out.println( "Caught in run: "  + ex);                            ex.printStackTrace();                        }                    }                };                t.start();                // Give t time to get going...                Thread.sleep( 100 );                t.stop();  // EXPECT COMPILER WARNING            catch  (Throwable t) {                System.out.println( "Caught in main: "  + t);                t.printStackTrace();            }            }

假设我们有如上一个工作线程,它的工作是数数,从1到1000000,我们的目标是在它进行数数的过程中,停止该线程的运作。如果我们按照上面的方式来调用thread.stop()方法,原则上是可以实现我们的目标的,根据SUN官方文档的解释,加上在上面的程序中,主线程只休眠了100ms,而工作线程从1数到1000000所花时间大概是4-5s,那么该工作线程应该只从1数到某个值(小于1000000),然后线程停止。 

但是根据运行结果来看,并非如此。

结果:

。。。

runing..99998
runing..99999

5

。。。

runing..99998
runing..99999
4

每次运行的结果都表明,工作线程并没有停止,而是每次都成功的数完数,然后正常中止,而不是由stop()方法进行终止的。这个是为什么呢?根据SUN的文档,原则上只要一调用thread.stop()方法,那么线程就会立即停止,并抛出ThreadDeath error,查看了Thread的源代码后才发现,原先Thread.stop0()方法是同步的,而我们工作线程的run()方法也是同步,那么这样会导致主线程和工作线程共同争用同一个锁(工作线程对象本身),由于工作线程在启动后就先获得了锁,所以无论如何,当主线程在调用t.stop()时,它必须要等到工作线程的run()方法执行结束后才能进行,结果导致了上述奇怪的现象。

把上述工作线程的run()方法的同步去掉,再进行执行,结果就如上述第一点描述的那样了

可能的结果:

runing..4149
runing..4150
runing..4151
runing..4152runing..4152Caught in run: java.lang.ThreadDeath

或者

runing..5245
runing..5246
runing..5247
runing..5248runing..5248Caught in run: java.lang.ThreadDeath

接下来是看看当调用thread.stop()时,被停止的线程会不会释放其所持有的锁,看如下代码:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 public  static  void  main(String[] args) {             final  Object lock =  new  Object();             try  {                 Thread t0 =  new  Thread() {                     public  void  run() {                         try  {                             synchronized  (lock) {                                 System.out.println( "thread->"  + getName()                                         " acquire lock." );                                 sleep( 3000 ); // sleep for 3s                                 System.out.println( "thread->"  + getName()                                         " release lock." );                             }                         catch  (Throwable ex) {                             System.out.println( "Caught in run: "  + ex);                             ex.printStackTrace();                         }                     }                 };                     Thread t1 =  new  Thread() {                     public  void  run() {                         synchronized  (lock) {                             System.out.println( "thread->"  + getName()                                     " acquire lock." );                         }                     }                 };                     t0.start();                 // Give t time to get going...                 Thread.sleep( 100 );                 //t0.stop();                 t1.start();             catch  (Throwable t) {                 System.out.println( "Caught in main: "  + t);                 t.printStackTrace();             }             }             public  static  void  main(String[] args) {        final  Object lock =  new  Object();        try  {            Thread t0 =  new  Thread() {                public  void  run() {                    try  {                        synchronized  (lock) {                            System.out.println( "thread->"  + getName()                                    " acquire lock." );                            sleep( 3000 ); // sleep for 3s                            System.out.println( "thread->"  + getName()                                    " release lock." );                        }                    catch  (Throwable ex) {                        System.out.println( "Caught in run: "  + ex);                        ex.printStackTrace();                    }                }            };            Thread t1 =  new  Thread() {                public  void  run() {                    synchronized  (lock) {                        System.out.println( "thread->"  + getName()                                " acquire lock." );                    }                }            };            t0.start();            // Give t time to get going...            Thread.sleep( 100 );            //t0.stop();            t1.start();        catch  (Throwable t) {            System.out.println( "Caught in main: "  + t);            t.printStackTrace();        }   }

当没有进行t0.stop()方法的调用时, 可以发现,两个线程争用锁的顺序是固定的。

输出:

thread->Thread-0 acquire lock.
thread->Thread-0 release lock.
thread->Thread-1 acquire lock.

但调用了t0.stop()方法后,(去掉上面的注释//t0.stop();),可以发现,t0线程抛出了ThreadDeath error并且t0线程释放了它所占有的锁。

输出:

thread->Thread-0 acquire lock.
thread->Thread-1 acquire lock.
Caught in run: java.lang.ThreadDeath
java.lang.ThreadDeath
 at java.lang.Thread.stop(Thread.java:715)
 at com.yezi.test.timeout.ThreadStopTest.main(ThreadStopTest.java:40)

从上面的程序验证结果来看,thread.stop()确实是不安全的。它的不安全主要是针对于第二点:释放该线程所持有的所有的锁。一般任何进行加锁的代码块,都是为了保护数据的一致性,如果在调用thread.stop()后导致了该线程所持有的所有锁的突然释放,那么被保护数据就有可能呈现不一致性,其他线程在使用这些被破坏的数据时,有可能导致一些很奇怪的应用程序错误。

如何正确停止线程

关于如何正确停止线程,这篇文章(how to stop thread)给出了一个很好的答案, 总结起来就下面3点(在停止线程时):

1. 使用violate boolean变量来标识线程是否停止
2. 停止线程时,需要调用停止线程的interrupt()方法,因为线程有可能在wait()或sleep(), 提高停止线程的即时性
3. 对于blocking IO的处理,尽量使用InterruptibleChannel来代替blocking IO
 

核心如下:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 If you are writing your own small thread then you should follow the following example code. private  volatile  Thread myThread; public  void  stopMyThread() {      Thread tmpThread = myThread;      myThread =  null ;      if  (tmpThread !=  null ) {          tmpThread.interrupt();      } } public  void  run() {      if  (myThread ==  null ) {         return // stopped before started.      }      try  {          // all the run() method's code goes here          ...          // do some work          Thread.yield();  // let another thread have some time perha<a href="/fw/photo.html" target="_blank">ps</a> to stop this one.          if  (Thread.currentThread().isInterrupted()) {             throw  new  InterruptedException( "Stopped by ifInterruptedStop()" );          }          // do some more work          ...      catch  (Throwable t) {         // log/handle all errors here      } }
    原文作者:d小袋鼠
    原文地址: https://blog.csdn.net/u010897406/article/details/49805587
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞