Java的守护线程与非守护线程

来源:互联网 发布:讲文明知礼仪手抄报 编辑:程序博客网 时间:2024/05/01 20:26

<什么是守护线程,什么是非守护线程>

Java有两种Thread:“守护线程Daemon”(守护线程)与“用户线程User”(非守护线程)。

用户线程:非守护线程包括常规的用户线程或诸如用于处理GUI事件的事件调度线程,Java虚拟机在它所有非守护线程已经离开后自动离开。

守护线程:守护线程则是用来服务用户线程的,比如说GC线程。如果没有其他用户线程在运行,那么就没有可服务对象,也就没有理由继续下去。(操作系统里面是没有所谓的守护线程的概念,只有守护进程一说,但是Java语言机制是构建在JVM的基础之上的,意思是Java平台把操作系统的底层给屏蔽起来,所以它可以在它自己的虚拟的平台里面构造出对 自己有利的机制,而语言或者说平台的设计者多多少少是受到Unix思想的影响,而守护线程机制又是对JVM这样的平台凑合,于是守护线程应运而生);

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

守护线程与用户线程的唯一区别是:其实User Thread线程和Daemon Thread守护线程本质上来说去没啥区别的,唯一的区别之处就在虚拟机的离开,当JVM中所有的线程都是守护线程的时候,JVM就可以退出了(如果User Thread全部撤离,那么Daemon Thread也就没啥线程好服务的了,所以虚拟机也就退出了);如果还有一个或以上的非守护线程则不会退出。(以上是针对正常退出,调用System.exit则必定会退出)。

举个例子:就像天上人间的保安 (守护线程),里面有牌位姑娘(非守护线程),他们是可以同时干着各自的活儿,但是 姑娘们要是都被JC带走了,那么门口的保安也就没有存在的意义了。


<怎样创建守护线程>

       守护线程与普通线程写法上基本没什么区别,调用线程对象的方法setDaemon(true),则可以将其设置为守护线程。
       1、thread.setDaemon(true)必须在thread.start()之前设置,你不能把正在运行的常规线程设置为守护线程,否则会跑出一个IllegalThreadStateException异常,如果线程是守护线程,则isDaemon方法返回true。(备注:这点与守护进程有着明显的区别,守护进程是创建后,让进程摆脱原会话的控制+让进程摆脱原进程组的控制+让进程摆脱原控制终端的控制;所以说寄托于虚拟机的语言机制跟系统级语言有着本质上面的区别)。
       2、在Daemon线程中产生的新线程也是Daemon的。  (这一点又是有着本质的区别了:守护进程fork()出来的子进程不再是守护进程,尽管它把父进程的进程相关信息复制过去了,但是子进程的进程的父进程不是init进程,所谓的守护进程本质上说就是“父进程挂掉,init收养,然后文件0,1,2都是/dev/null,当前目录到/”)
       3、不是所有的应用都可以分配给Daemon线程来进行服务,比如读写操作或者计算逻辑。因为在Daemon Thread还没来的及进行操作时,虚拟机可能已经退出了。

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


<守护线程调度示例>

//示例1:完成文件输出的守护线程任务
import java.io.*; 

public class TestDemo { 

    public static void main(String[] args) throws InterruptedException   {  
        Runnable tr = new TestRunnable();  
        Thread thread = new Thread(tr);  
        thread.setDaemon(true); //设置守护线程  
        thread.start(); //开始执行分进程  
    } 

class TestRunnable implements Runnable{  
    public void run(){  
          try{  
                  Thread.sleep(1000);//守护线程阻塞1秒后运行  
                  File f=new File("daemon.txt");  
                  FileOutputStream os=new FileOutputStream(f,true);  
                  os.write("daemon".getBytes());  
           }  catch(IOException e1){  
                  e1.printStackTrace();  
           }  catch(InterruptedException e2){  
                  e2.printStackTrace();  
           }  
    }  
 }

}  
运行结果:文件daemon.txt中没有"daemon"字符串。
但是如果把thread.setDaemon(true); //设置守护线程注释掉,文件daemon.txt是可以被写入daemon字符串的

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


//示例2:线程的调度-守护线程
public class Test { 
         public  static  void main(String[] args) { 
                Thread t1 =  new MyCommon(); 
                Thread t2 =  new Thread( new MyDaemon()); 
                t2.setDaemon( true); //设置为守护线程 
                t2.start(); 
                t1.start(); 
        } 


class MyCommon extends Thread { 
         public  void run() { 
                 for ( int i = 0; i < 5; i++) { 
                        System.out.println( "线程1第" + i +  "次执行!"); 
                         try { 
                                Thread.sleep(7); 
                        }  catch (InterruptedException e) { 
                                e.printStackTrace(); 
                        } 
                } 
        } 


class MyDaemon implements Runnable { 
         public  void run() { 
                 for ( long i = 0; i < 9999999L; i++) { 
                        System.out.println( "后台线程第" + i +  "次执行!"); 
                         try { 
                                Thread.sleep(7); 
                        }  catch (InterruptedException e) { 
                                e.printStackTrace(); 
                        } 
                } 
        } 
}

 
后台线程第0次执行! 
线程1第0次执行! 
线程1第1次执行! 
后台线程第1次执行! 
后台线程第2次执行! 
线程1第2次执行! 
线程1第3次执行! 
后台线程第3次执行! 
线程1第4次执行! 
后台线程第4次执行! 
后台线程第5次执行! 
后台线程第6次执行! 
后台线程第7次执行! 

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

0 0
原创粉丝点击