java concurrency: daemon线程

daemon线程的概念

在学习操作系统概念的时候,我们就曾听说过daemon的概念。daemon本身指的是在后台运行的进程或者线程,一般用来提供某些不需要与用户直接交互的服务,有点像我们见到的一些系统服务。在java线程中,一般可以分为两类,一类是普通的线程,就是那些我们通过常用的Thread类或者Runnable接口实现并启动的类。还有一类是daemon线程。这种线程也通过和创建普通线程同样的方式来创建,不过需要通过setDaemon方法设置为daemon线程。

daemon线程有几个典型的特征:

1. daemon线程是运行于后台的,不和用户直接交互。

2. 相对普通的线程来说,daeomn线程可以说是普通线程的下属,它的优先级要低一些。

3. daemon线程具有一定程度的继承性,这个继承性不是指类的继承,而是指当一个daemon线程创建一个新线程的话,这个新创建的线程就默认为daemon线程了。

4. 和普通线程的退出机制不同,在jvm即将关闭的时候,普通线程需要执行一系列的记录或者退出方法,比如需要执行finalize方法的则需要执行这部分,有finally块的代码也必须执行。而daemon线程退出的时候,jvm直接将它丢弃并退出,里面就算有finally块定义的代码也不会被执行。

一个daemon线程的示例

创建或者使用daemon线程一般包括一下3个步骤:

1. 如果线程本身就是daemon线程了,那么通过常规线程创建手段创建出来的就已经是daemon线程。

2. 如果通过普通线程创建创建出来的话,需要在启动线程前调用setDaemon()方法。切记,一定要在调用start()方法前。

3.我们可以通过调用isDaemon()方法来判断一个线程是否为daeomn.

下面是示例代码:

Java代码  

  1. class MyDaemon implements Runnable
  2. {
  3. Thread thrd;
  4. MyDaemon()
  5. {
  6. // Create the thread
  7. thrd = new Thread(this);
  8. // Set to daemon
  9. thrd.setDaemon(true);
  10. // Start the thread
  11. thrd.start();
  12. }
  13. public void run()
  14. {
  15. try
  16. {
  17. for(;;)
  18. {
  19. System.out.print(".");
  20. Thread.sleep(1000);
  21. }
  22. }
  23. catch(InterruptedException exc)
  24. {
  25. System.out.println("MyDaemon interrupted.");
  26. }
  27. }
  28. }
  29. class DaemonDemo
  30. {
  31. public static void main(String[] args)
  32. {
  33. MyDaemon dt = new MyDaemon();
  34. if(dt.thrd.isDaemon())
  35. System.out.println("dt is a daemon thread.");
  36. System.out.println("Sleeping in main thread.");
  37. try
  38. {
  39. Thread.sleep(10000);
  40. }
  41. catch(InterruptedException exc)
  42. {
  43. System.out.println("Main thread interrupted.");
  44. }
  45. System.out.println("\nMain thread ending.");
  46. }
  47. }

在上面的代码中,我们实现Runnable接口定义一个MyDaemon类,在构造函数中调用setDaemon()设置该线程为daemon.在main方法中启动MyDaemon线程之后,main方法结束时daemon线程也自动结束了。上述代码的执行结果如下:

Java代码  

  1. dt is a daemon thread.
  2. Sleeping in main thread.
  3. ..........
  4. Main thread ending.

一个比较有意思的事情就是,如果我们将前面代码里setDaemon()方法的这一行注释了,也就是说将该线程设置为普通的用户线程,再执行上面的代码时,我们会发现,程序不会终止,会一直执行下去,虽然主线程已经结束了。其输出的结果会像如下所示:

Java代码  

  1. Sleeping in main thread.
  2. ..........
  3. Main thread ending.
  4. .......................

设置daemon线程的意义

从前面的理解来看,daemon线程有点像一个我们画的草图,随时可以丢弃。它一般是用于在jvm关闭的时候,我们需要启动一些线程做一些辅助性的工作,但是我们又不希望这种工作妨碍到jvm正常关闭。和其他正常的线程来说,或许我们应该称之为不碍事的线程更合适。

daemon线程的一些应用

daemon线程在java中有很多地方用到,一个典型的就是垃圾回收的GC线程。另外如果我们需要用线程做一些比如时间提醒,标记等事情,采用daemon线程也是一个比较合适的选择。

又一个daemon的示例

根据前面的理解,我们再尝试一个更复杂的daemon应用。假定我们在一个应用中需要定义一些时间通知,比如说设置在某个指定的时候弹出一个会议通知的提醒。这是一个比较常见的场景,可以用daemon线程来实现。

具体的代码实现如下:

Java代码  

  1. import java.util.*;
  2. class Reminder implements Runnable
  3. {
  4. Calendar reminderTime;
  5. String message;
  6. Reminder(String msg, int delay)
  7. {
  8. message = msg;
  9. // Get the current time and date.
  10. reminderTime = Calendar.getInstance();
  11. // Add the delay to the time and date.
  12. reminderTime.add(Calendar.SECOND, delay);
  13. System.out.printf("Reminder set for %tD %1$tr\n", reminderTime);
  14. // Create the reminder thread.
  15. Thread dThrd = new Thread(this);
  16. // Set to daemon
  17. dThrd.setDaemon(true);
  18. // Start execution.
  19. dThrd.start();
  20. }
  21. // Run the reminder.
  22. public void run()
  23. {
  24. try
  25. {
  26. for(;;)
  27. {
  28. // Get the current time and date.
  29. Calendar curTime = Calendar.getInstance();
  30. // See if it‘s time for the reminder.
  31. if(curTime.compareTo(reminderTime) >= 0)
  32. {
  33. System.out.println("\n" + message + "\n");
  34. break;
  35. }
  36. Thread.sleep(1000);
  37. }
  38. }
  39. catch(InterruptedException exc)
  40. {
  41. System.out.println("Reminder interrupted.");
  42. }
  43. }
  44. }
  45. class ReminderDemo
  46. {
  47. public static void main(String[] args)
  48. {
  49. // Get a reminder 2 seconds from now.
  50. Reminder mt = new Reminder("Call Harry", 2);
  51. // Keep the main thread alive for 20 seconds.
  52. for(int i = 0; i < 20; i++)
  53. {
  54. try
  55. {
  56. Thread.sleep(1000);
  57. }
  58. catch(InterruptedException exc)
  59. {
  60. System.out.println("Main thread interrupted.");
  61. }
  62. System.out.print(".");
  63. }
  64. System.out.println("\nMain thread ending.");
  65. }
  66. }

我们设置了一个daemon线程,在一个无限循环里比较当前时间和指定的时间,在达到指定的时间时则显示一个提醒消息。上述代码的输出如下:

Java代码  

  1. Reminder set for 10/21/12 01:57:10 PM
  2. ..
  3. Call Harry
  4. ..................
  5. Main thread ending.

总结

悄悄的,daemon线程走了,正如daemon线程悄悄的来;挥一挥手,带不走jvm的一片云彩:)

时间: 2024-10-19 02:48:29

java concurrency: daemon线程的相关文章

【转】关于Java的Daemon线程的理解

原文地址:http://www.cnblogs.com/ChrisWang/archive/2009/11/28/1612815.html 关于Java的Daemon线程的理解 网上对Java的Daemon线程的说法很多,看的人头晕. 所以自己就来总结一下: Java语言自己可以创建两种进程“用户线程”和“守护线程” 用户线程:就是我们平时创建的普通线程. 守护线程:主要是用来服务用户线程. 那么如何来区分这两种线程呢? 其实在JDK的文档中已经说明的很清楚了: * The Java Virtu

深入浅出 Java Concurrency (35): 线程池 part 8 线程池的实现及原理 (3)[转]

线程池任务执行结果 这一节来探讨下线程池中任务执行的结果以及如何阻塞线程.取消任务等等. 1 package info.imxylz.study.concurrency.future;2 3 public class SleepForResultDemo implements Runnable {4 5     static boolean result = false;6 7     static void sleepWhile(long ms) {8         try {9      

深入浅出 Java Concurrency (33): 线程池 part 6 线程池的实现及原理 (1)[转]

线程池数据结构与线程构造方法 由于已经看到了ThreadPoolExecutor的源码,因此很容易就看到了ThreadPoolExecutor线程池的数据结构.图1描述了这种数据结构. 图1 ThreadPoolExecutor 数据结构 其实,即使没有上述图形描述ThreadPoolExecutor的数据结构,我们根据线程池的要求也很能够猜测出其数据结构出来. 线程池需要支持多个线程并发执行,因此有一个线程集合Collection<Thread>来执行线程任务: 涉及任务的异步执行,因此需要

深入浅出 Java Concurrency (28): 线程池 part 1 简介[转]

从这一节开始正式进入线程池的部分.其实整个体系已经拖了很长的时间,因此后面的章节会加快速度,甚至只是一个半成品或者简单化,以后有时间的慢慢补充.完善. 其实线程池是并发包里面很重要的一部分,在实际情况中也是使用很多的一个重要组件. 下图描述的是线程池API的一部分.广义上的完整线程池可能还包括Thread/Runnable.Timer/TimerTask等部分.这里只介绍主要的和高级的API以及架构和原理. 大多数并发应用程序是围绕执行任务(Task)进行管理的.所谓任务就是抽象.离散的工作单元

深入浅出 Java Concurrency (34): 线程池 part 7 线程池的实现及原理 (2)[转]

线程池任务执行流程 我们从一个API开始接触Executor是如何处理任务队列的. java.util.concurrent.Executor.execute(Runnable) Executes the given task sometime in the future. The task may execute in a new thread or in an existing pooled thread. If the task cannot be submitted for execut

深入浅出 Java Concurrency (30): 线程池 part 3 Executor 生命周期[转]

我们知道线程是有多种执行状态的,同样管理线程的线程池也有多种状态.JVM会在所有线程(非后台daemon线程)全部终止后才退出,为了节省资源和有效释放资源关闭一个线程池就显得很重要.有时候无法正确的关闭线程池,将会阻止JVM的结束. 线程池Executor是异步的执行任务,因此任何时刻不能够直接获取提交的任务的状态.这些任务有可能已经完成,也有可能正在执行或者还在排队等待执行.因此关闭线程池可能出现一下几种情况: 平缓关闭:已经启动的任务全部执行完毕,同时不再接受新的任务 立即关闭:取消所有正在

深入浅出 Java Concurrency (36): 线程池 part 9 并发操作异常体系[转]

并发包引入的工具类很多方法都会抛出一定的异常,这些异常描述了任务在线程池中执行时发生的例外情况,而通常这些例外需要应用程序进行捕捉和处理. 例如在Future接口中有如下一个API: java.util.concurrent.Future.get(long, TimeUnit) throws InterruptedException, ExecutionException, TimeoutException; 在前面的章节中描述了Future类的具体实现原理.这里不再讨论,但是比较好奇的抛出的三

深入浅出 Java Concurrency (29): 线程池 part 2 Executor 以及Executors[转]

Java里面线程池的顶级接口是Executor,但是严格意义上讲Executor并不是一个线程池,而只是一个执行线程的工具.真正的线程池接口是ExecutorService. 下面这张图完整描述了线程池的类体系结构. 首先Executor的execute方法只是执行一个Runnable的任务,当然了从某种角度上将最后的实现类也是在线程中启动此任务的.根据线程池的执行策略最后这个任务可能在新的线程中执行,或者线程池中的某个线程,甚至是调用者线程中执行(相当于直接运行Runnable的run方法).

Java系列笔记 - 线程

1,线程原理和概念 当代操作系统,大多数都支持多任务处理.对于多任务的处理,有两个常见的概念:进程和线程.      进程是操作系统分配资源的单位,这里的资源包括CPU.内存.IO.磁盘等等设备,进程之间切换时,操作系统需要分配和回收这些资源,所以其开销相对较大(远大于线程切换):      线程是CPU分配时间的单位,理论上,每个进程至少包含一个线程,每个线程都寄托在一个进程中.一个线程相当于是一个进程在内存中的某个代码段,多个线程在切换时,CPU会根据其优先级和相互关系分配时间片.除时间切换