Java多线程B 线程的生命周期和状态控制

5、守护线程

守护线程与普通线程写法上基本么啥区别,调用线程对象的方法setDaemon(true),则可以将其设置为守护线程。

 

守护线程使用的情况较少,但并非无用,举例来说,JVM的垃圾回收、内存管理等线程都是守护线程。还有就是在做数据库应用时候,使用的数据库连接池,连接池本身也包含着很多后台线程,监控连接个数、超时时间、状态等等。

 

setDaemon方法的详细说明:
public final void setDaemon(boolean on)将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。    
该方法必须在启动线程前调用。 该方法首先调用该线程的 checkAccess 方法,且不带任何参数。这可能抛出SecurityException(在当前线程中)。   
  参数: 
    on - 如果为 true,则将该线程标记为守护线程。    
  抛出:    
    IllegalThreadStateException - 如果该线程处于活动状态。    
    SecurityException - 如果当前线程无法修改该线程。

[java] view plaincopy

1.       

4.     public class Test  

5.             public static void main(String[] args)  

6.                     Thread t1 new MyCommon();  

7.                     Thread t2 new Thread(new MyDaemon());  

8.                     t2.setDaemon(true);        //设置为守护线程  

9.       

10.                  t2.start();  

11.                  t1.start();  

12.           

13.   

14.    

15.  class MyCommon extends Thread  

16.          public void run()  

17.                  for (int 05i++)  

18.                          System.out.println("线程1" "次执行!");  

19.                          try  

20.                                  Thread.sleep(7);  

21.                          catch (InterruptedException e)  

22.                                  e.printStackTrace();  

23.                           

24.                   

25.           

26.   

27.    

28.  class MyDaemon implements Runnable  

29.          public void run()  

30.                  for (long 09999999L; i++)  

31.                          System.out.println("后台线程第" "次执行!");  

32.                          try  

33.                                  Thread.sleep(7);  

34.                          catch (InterruptedException e)  

35.                                  e.printStackTrace();  

36.                           

37.                   

38.           

39.   


执行结果:

[java] view plaincopy

1.     后台线程第0次执行!  

2.     线程10次执行!  

3.     线程11次执行!  

4.     后台线程第1次执行!  

5.     后台线程第2次执行!  

6.     线程12次执行!  

7.     线程13次执行!  

8.     后台线程第3次执行!  

9.     线程14次执行!  

10.  后台线程第4次执行!  

11.  后台线程第5次执行!  

12.  后台线程第6次执行!  

13.  后台线程第7次执行!   

从上面的执行结果可以看出:前台线程是保证执行完毕的,后台线程还没有执行完毕就退出了。

 

实际上:JRE判断程序是否执行结束的标准是所有的前台执线程行完毕了,而不管后台线程的状态,因此,在使用后台县城时候一定要注意这个问题。




守护线程的用途:

守护线程通常用于执行一些后台作业,例如在你的应用程序运行时播放背景音乐,在文字编辑器里做自动语法检查、自动保存等功能。Java的垃圾回收也是一个守护线程。守护线

的好处就是你不需要关心它的结束问题。例如你在你的应用程序运行的时候希望播放背景音乐,如果将这个播放背景音乐的线程设定为非守护线程,那么在用户请求退出的时候,

不仅要退出主线程,还要通知播放背景音乐的线程退出;如果设定为守护线程则不需要了。


6、如何结束一个线程

Thread.stop()Thread.suspendThread.resumeRuntime.runFinalizersOnExit这些终止线程运行的方法已经被废弃了,使用它们是极端不安全的!想要安全有效的结束一个线程,可以使用下面的方法。

1、正常执行完run方法,然后结束掉

2、控制循环条件和判断条件的标识符来结束掉线程

比如说run方法这样写:

[java] view plaincopy

1.     class MyThread extends Thread  

2.         int i=0 

3.         @Override  

4.         public void run()  

5.             while (true 

6.                 if(i==10 

7.                     break 

8.                 i++;  

9.                 System.out.println(i);  

10.                

11.           

12.       

13.   

或者

[java] view plaincopy

1.     class MyThread extends Thread  

2.         int i=0 

3.         boolean next=true 

4.         @Override  

5.         public void run()  

6.             while (next)  

7.                 if(i==10 

8.                     next=false 

9.                 i++;  

10.              System.out.println(i);  

11.           

12.       

13.   


或者

[java] view plaincopy

1.     class MyThread extends Thread  

2.         int i=0 

3.         @Override  

4.         public void run()  

5.             while (true 

6.                 if(i==10 

7.                     return 

8.                 i++;  

9.                 System.out.println(i);  

10.           

11.       

12.   


只要保证在一定的情况下,run方法能够执行完毕即可。而不是while(true)的无线循环。

 

3、使用interrupt结束一个线程。

诚然,使用第2中方法的标识符来结束一个线程,是一个不错的方法,但是如果,该线程是处于sleepwaitjoin的状态的时候,while循环就不会执行,那么我们的标识符就无用武之地了,当然也不能再通过它来结束处于这3种状态的线程了。

可以使用interrupt这个巧妙的方式结束掉这个线程。

我们看看sleepwaitjoin方法的声明:

[java] view plaincopy

1.     public final void wait() throws InterruptedException  

[java] view plaincopy

1.     public static native void sleep(long millis) throws InterruptedException  

[java] view plaincopy

1.     public final void join() throws InterruptedException  

可以看到,这三者有一个共同点,都抛出了一个InterruptedException的异常。

在什么时候会产生这样一个异常呢?

每个Thread都有一个中断状状态,默认为false。可以通过Thread对象的isInterrupted()方法来判断该线程的中断状态。可以通过Thread对象的interrupt()方法将中断状态设置为true

当一个线程处于sleepwaitjoin这三种状态之一的时候,如果此时他的中断状态为true,那么它就会抛出一个InterruptedException的异常,并将中断状态重新设置为false

看下面的简单的例子:

[java] view plaincopy

1.     public class Test1  

2.         public static void main(String[] args) throws InterruptedException  

3.             MyThread thread=new MyThread();  

4.             thread.start();  

5.          

6.      

7.       

8.     class MyThread extends Thread  

9.         int i=1 

10.      @Override  

11.      public void run()  

12.          while (true 

13.              System.out.println(i);  

14.              System.out.println(this.isInterrupted());  

15.              try  

16.                  System.out.println("我马上去sleep");  

17.                  Thread.sleep(2000);  

18.                  this.interrupt();  

19.              catch (InterruptedException e)  

20.                  System.out.println("异常捕获了"+this.isInterrupted());  

21.                  return 

22.               

23.              i++;  

24.           

25.       

26.   

测试结果:

[java] view plaincopy

1.     1  

2.     false  

3.     我马上去sleep  

4.     2  

5.     true  

6.     我马上去sleep  

7.     异常捕获了false  

可以看到,首先执行第一次while循环,在第一次循环中,睡眠2秒,然后将中断状态设置为true。当进入到第二次循环的时候,中断状态就是第一次设置的true,当它再次进入sleep的时候,马上就抛出了InterruptedException异常,然后被我们捕获了。然后中断状态又被重新自动设置为false了(从最后一条输出可以看出来)。


 

所以,我们可以使用interrupt方法结束一个线程。具体使用如下:

[java] view plaincopy

1.     public class Test1  

2.         public static void main(String[] args) throws InterruptedException  

3.             MyThread thread=new MyThread();  

4.             thread.start();  

5.             Thread.sleep(3000);  

6.             thread.interrupt();  

7.          

8.      

9.       

10.  class MyThread extends Thread  

11.      int i=0 

12.      @Override  

13.      public void run()  

14.          while (true 

15.              System.out.println(i);  

16.              try  

17.                  Thread.sleep(1000);  

18.              catch (InterruptedException e)  

19.                  System.out.println("中断异常被捕获了");  

20.                  return 

21.               

22.              i++;  

23.           

24.       

25.   

多测试几次,会发现一般有两种执行结果:

[java] view plaincopy

1.     0  

2.     1  

3.     2  

4.     中断异常被捕获了  

或者

[java] view plaincopy

1.     0  

2.     1  

3.     2  

4.     3  

5.     中断异常被捕获了  




这两种结果恰恰说明了  只要一个线程的中断状态一旦为true,只要它进入sleep等状态,或者处于sleep状态,立马回抛出InterruptedException异常。

第一种情况,是当主线程从3秒睡眠状态醒来之后,调用了子线程的interrupt方法,此时子线程正处于sleep状态,立马抛出InterruptedException异常。

第一种情况,是当主线程从3秒睡眠状态醒来之后,调用了子线程的interrupt方法,此时子线程还没有处于sleep状态。然后再第3while循环的时候,在此进入sleep状态,立马抛出InterruptedException异常。

 

 

原文地址:https://www.cnblogs.com/luckForever/p/7254314.html