java 中有两类线程:用户线程和守护线程。
守护线程的作用是为用户线程的运行提供服务,比如说 GC 线程。
输出:
守护线程启动 这个线程是守护线程吗?Yes
关键在于 thread.setDaemon(true) 这段代码。
特点一: setDaemon(true) 必须在 start() 之前,否则会有异常。你不能把正在运行的常规线程设置为守护线程。
特点二: 守护线程存在的目的是为用户线程提供服务,因此如果用户线程全部撤离,那么守护线程也就没什么存在的必要了,所以虚拟机也就退出了。所以守护线程中的 finally 块不一定会执行。
下面举例验证:
public class DaemonTest_02 { public static void main(String[] args) { Thread thread = new Thread(new DaemonRunner(), "DeamonRunner"); thread.setDaemon(true); thread.start(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } static class DaemonRunner implements Runnable { @Override public void run() { try { System.out.println("守护线程启动"); System.out.println("这个线程是守护线程吗?" + (Thread.currentThread().isDaemon() ? "Yes" : "No")); Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } finally { System.out.println("进入 finally 方法"); } } }}可能的一种输出:
守护线程启动 这个线程是守护线程吗?Yes
main 方法执行完毕后虚拟机中已经没有用户线程了,虚拟机需要退出,所有守护线程立即终止,但是 finally 块并没有执行。因此在构建守护线程时,不能依靠 finally 块中的内容来确保执行关闭或清理资源的逻辑。
特点三: 守护线程创建的新线程也是守护线程。
举例说明:
public class DaemonTest_03 { public static void main(String[] args) { Thread thread = new Thread(new DaemonRunner(), "DeamonRunner"); thread.setDaemon(true); thread.start(); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } } static class DaemonRunner implements Runnable { @Override public void run() { System.out.println("守护线程启动"); System.out.println("这个线程是守护线程吗:" + (Thread.currentThread().isDaemon()?"Yes":"No")); try { Thread thread = new Thread(new Runner(), "Runner"); // thread.setDaemon(true); 注意这里并没有明确设置新线程为守护线程 thread.start(); Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } } static class Runner implements Runnable { @Override public void run() { System.out.println("新线程启动"); System.out.println("这个新线程是守护线程吗:" + (Thread.currentThread().isDaemon()?"Yes":"No")); } }}输出:
守护线程启动 这个线程是守护线程吗:Yes 新线程启动 这个新线程是守护线程吗:Yes
新闻热点
疑难解答