Java daemon thread

Foreword:

There are two types of threads in Java: User Thread (user thread) and Daemon Thread (daemon thread). As long as any non-daemon thread in the current JVM instance has not ended, the daemon thread will all work; only when the last non-daemon thread ends When the daemon thread ends its work with the JVM. The role of Daemon is to provide convenient services for the operation of other threads. The most typical application of daemon threads is GC (garbage collector), which is a very competent guardian.

There is almost no difference between User and Daemon, the only difference is the departure of the virtual machine: if the User Thread has all exited, only the Daemon Thread exists, and the virtual machine will exit. Because there is no guardian, the Daemon has no work to do, and there is no need to continue running the program.

Demo: Daemon threads are not only provided inside the virtual machine, users can also set daemon threads themselves when writing programs. The following method is used to set the daemon thread.

     Thread daemonTread = new Thread();  
       
      // Set daemonThread as a daemon thread, default false (non-daemon thread)  
      // To be set to true before thread start
     daemonThread.setDaemon(true);  
       
     // Verify whether the current thread is a daemon thread, return true to be a daemon thread  
     daemonThread.isDaemon();  

 There are a few points to note here:
(1) thread.setDaemon(true) must be set before thread.start(), otherwise an IllegalThreadStateException will be thrown. You cannot set a running regular thread as a daemon thread.
(2) The new thread generated in the Daemon thread is also Daemon's.
(3) Don't think that all applications can be assigned to Daemon for services, such as read and write operations or computing logic.

Because you can't know whether the Daemon has completed the expected service task before all the Users have completed. Once the User exits, a large amount of data may not have time to be read in or written out, and the calculation task may run multiple times with different results. This is devastating to the program. The reason for this result has already been said: once all User Threads leave, the virtual machine also quits running.

//Complete the daemon thread task of file output  
import java.io. *;     
    
class TestRunnable implements Runnable{     
    public void run(){     
               try{     
                  Thread.sleep(1000);//The daemon thread runs after blocking for 1 second     
                  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();     
           }     
    }     
}     
public class TestDemo2{     
    public static void main(String[] args) throws InterruptedException     
    {     
        Runnable tr=new TestRunnable();     
        Thread thread=new Thread(tr);     
                thread.setDaemon(true); //Set the daemon thread     
        thread.start(); //Start executing sub-process     
    }     
}     
//Running result: There is no "daemon" string in the file daemon.txt.

 See, how terrible it is to wrap the input and output logic into a daemon thread, the string is not written to the specified file. The reason is also very simple, until the main thread is finished, the daemon thread is still blocked for 1 second. At this time, the main thread runs quickly, the virtual machine exits, the Daemon stops the service, and the output operation naturally fails.

public class Test {
	public static void main(String []args) {
		Thread t1 = new MyCommon();
		Thread t2 = new Thread(new MyDaemon());
		t2.setDaemon(true); // set to daemon thread
		t2.start();
		t1.start();
	}
}

class MyCommon extends Thread {
	public void run() {
		for (int i = 0; i < 5; i++) {
			System.out.println("Thread 1's "+i+" execution!");
			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("Background thread " + i + " time execution!");
			try {
				Thread.sleep(7);
			} catch (InterruptedException e) {
				e.printStackTrace ();
			}
		}
	}
}

      The 0th execution of the background thread!
  Thread 1 executes for the 0th time!
  Thread 1 executes for the first time!
  The first execution of the background thread!
  The background thread is executed for the second time!
  Thread 1 executes for the second time!
  Thread 1 executes for the 3rd time!
  The background thread is executed for the 3rd time!
  Thread 1 executes for the 4th time!
  The 4th execution of the background thread!
  The 5th execution of the background thread!
  The 6th execution of the background thread!
  The 7th execution of the background thread!
  Process finished with exit code 0
  It can be seen from the above execution results that the
  foreground thread is guaranteed to complete the execution, and the background thread exits before the execution is completed.
  In fact: the criterion for JRE to judge whether the program execution ends is that all foreground execution threads are completed, regardless of the status of the background threads. Therefore, you must pay attention to this problem when using the background county.

Supplementary Note:
Definition: Daemon thread -- also known as "service thread", will automatically leave when there is no user thread to serve.
Priority: The daemon thread has a lower priority and is used to provide services to other objects and threads in the system.
Setting: Set the thread as a "daemon thread" by setDaemon(true); the way to set a user thread as a
daemon thread is to use the setDaemon method of the thread object before the thread object is created.
Example: The garbage collection thread is a classic daemon thread. When there are no more running Threads in our program
, the program will no longer generate garbage, and the garbage collector will have nothing to do, so when the garbage collection thread is the
JVM When the only remaining threads are left, the garbage collection thread automatically leaves. It always runs in a low-level state for
real-time monitoring and management of recyclable resources in the system.
Life cycle: Daemon is a special process that runs in the background. It is independent of the controlling terminal and
periodically performs some task or waits for some event to occur. That is to
say, the daemon thread does not depend on the terminal, but depends on the system and "lives and dies" with the system. What does Java's daemon thread
look like? When all threads in the JVM are daemon threads, the JVM can exit; if there are one
or more non-daemon threads, the JVM will not exit.

Practical application example:

在使用长连接的comet服务端推送技术中,消息推送线程设置为守护线程,服务于ChatServlet的servlet用户线程,在servlet的init启动消息线程,servlet一旦初始化后,一直存在服务器,servlet摧毁后,消息线程自动退出

容器收到一个Servlet请求,调度线程从线程池中选出一个工作者线程,将请求传递给该工作者线程,然后由该线程来执行Servlet的 service方法。当这个线程正在执行的时候,容器收到另外一个请求,调度线程同样从线程池中选出另一个工作者线程来服务新的请求,容器并不关心这个请 求是否访问的是同一个Servlet.当容器同时收到对同一个Servlet的多个请求的时候,那么这个Servlet的service()方法将在多线 程中并发执行。
Servlet容器默认采用单实例多线程的方式来处理请求,这样减少产生Servlet实例的开销,提升了对请求的响应时间,对于Tomcat可以在server.xml中通过<Connector>元素设置线程池中线程的数目。
如图:

 
为什么要用守护线程?

我 们知道静态变量是ClassLoader级别的,如果Web应用程序停止,这些静态变量也会从JVM中清除。但是线程则是JVM级别的,如果你在Web 应用中启动一个线程,这个线程的生命周期并不会和Web应用程序保持同步。也就是说,即使你停止了Web应用,这个线程依旧是活跃的。正是因为这个很隐晦 的问题,所以很多有经验的开发者不太赞成在Web应用中私自启动线程。

如果我们手工使用JDK Timer(Quartz的Scheduler),在Web容器启动时启动Timer,当Web容器关闭时,除非你手工关闭这个Timer,否则Timer中的任务还会继续运行!

下面通过一个小例子来演示这个“诡异”的现象,我们通过ServletContextListener在Web容器启动时创建一个Timer并周期性地运行一个任务: 

    //代码清单StartCycleRunTask:容器监听器  
    package com.baobaotao.web;  
    import java.util.Date;  
    import java.util.Timer;  
    import java.util.TimerTask;  
    import javax.servlet.ServletContextEvent;  
    import javax.servlet.ServletContextListener;  
    public class StartCycleRunTask implements ServletContextListener ...{  
        private Timer timer;  
        public void contextDestroyed(ServletContextEvent arg0) ...{  
            // ②该方法在Web容器关闭时执行  
            System.out.println("Web应用程序启动关闭...");  
        }  
        public void contextInitialized(ServletContextEvent arg0) ...{  
             //②在Web容器启动时自动执行该方法  
            System.out.println("Web应用程序启动...");  
            timer = new Timer();//②-1:创建一个Timer,Timer内部自动创建一个背景线程  
            TimerTask task = new SimpleTimerTask();  
            timer.schedule(task, 1000L, 5000L); //②-2:注册一个5秒钟运行一次的任务  
        }  
    }  
    class SimpleTimerTask extends TimerTask ...{//③任务  
        private int count;  
        public void run() ...{  
            System.out.println((++count)+"execute task..."+(new Date()));  
        }  
    }  

 在web.xml中声明这个Web容器监听器:<?xml version="1.0" encoding="UTF-8"?>
<web-app>

<listener>
<listener-class>com.baobaotao.web.StartCycleRunTask</listener-class>
</listener>
</web-app>

在Tomcat中部署这个Web应用并启动后,你将看到任务每隔5秒钟执行一次。
运行一段时间后,登录Tomcat管理后台,将对应的Web应用(chapter13)关闭。

转到Tomcat控制台,你将看到虽然Web应用已经关闭,但Timer任务还在我行我素地执行如故——舞台已经拆除,戏子继续表演:

我们可以通过改变清单StartCycleRunTask的代码,在contextDestroyed(ServletContextEvent arg0)中添加timer.cancel()代码,在Web容器关闭后手工停止Timer来结束任务。

Spring为JDK Timer和Quartz Scheduler所提供的TimerFactoryBean和SchedulerFactoryBean能够和Spring容器的生命周期关联,在 Spring容器启动时启动调度器,而在Spring容器关闭时,停止调度器。所以在Spring中通过这两个FactoryBean配置调度器,再从 Spring IoC中获取调度器引用进行任务调度将不会出现这种Web容器关闭而任务依然运行的问题。而如果你在程序中直接使用Timer或Scheduler,如不 进行额外的处理,将会出现这一问题。

原文作者:http://blog.csdn.net/shimiso

Java 技术交流群:426554356期待您的加入

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326902474&siteId=291194637