一、daemonthread概述
daemon thread(守护线程)是一种在后台运行的线程。当所有的非守护线程结束时,守护线程随着JVM一起关闭。一般情况下,守护线程用于执行一些后台任务,比如定期清理垃圾、检查网络连接等。
守护线程可以通过 Thread.setDaemon(true) 来设置线程为守护线程,但是必须在调用 start() 方法启动线程之前设置。
public class DaemonThreadExample extends Thread { public void run() { if (Thread.currentThread().isDaemon()) { System.out.println("This is a daemon thread."); } else { System.out.println("This is not a daemon thread."); } } public static void main(String[] args) { DaemonThreadExample t1 = new DaemonThreadExample(); DaemonThreadExample t2 = new DaemonThreadExample(); t1.setDaemon(true); // 设置为守护线程 t1.start(); t2.start(); } }
二、daemon thread的特点
守护线程有以下特点:
1.守护线程在非守护线程结束时自动结束
当所有的非守护线程结束时,JVM 会检查是否还有守护线程在运行,如果没有,则自动退出。因此,守护线程不应该被用来执行需要确保完成的操作,比如写文件等。
2.守护线程自动被设置为低优先级
当一个线程启动时,它会继承其父线程的优先级。但是,守护线程会被自动设置为低优先级,这意味着它们会被优先调度用于执行回收垃圾等不太重要的任务。
3.守护线程不能持有任何锁
因为守护线程会在非守护线程结束时自动结束,如果它持有锁,那么其他线程就无法再获得该锁,这会造成死锁。
三、如何使用daemonthread
1.线程池中使用
可以在线程池中创建守护线程,这样就可以方便地管理和控制守护线程。
public class DaemonThreadPoolExample { public static void main(String[] args) throws InterruptedException { ExecutorService executorService = Executors.newCachedThreadPool(new ThreadFactory() { @Override public Thread newThread(Runnable r) { Thread thread = new Thread(r); thread.setDaemon(true); // 设置为守护线程 return thread; } }); executorService.submit(() -> { while (true) { System.out.println("daemon thread is running in the thread pool."); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }); Thread.sleep(5000); executorService.shutdown(); } }
2.后台任务调度
比如定期检查数据库连接池是否满足要求等任务可以使用守护线程来执行。下面的代码演示了如何使用 ScheduledThreadPoolExecutor 定期执行一段后台任务。
public class DaemonScheduledExecutorExample { public static void main(String[] args) { ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1); executor.setRemoveOnCancelPolicy(true); // 守护线程需要设置此属性为true,表示从任务队列中删除已取消的任务 ScheduledFuture future = executor.scheduleAtFixedRate(new Runnable() { @Override public void run() { System.out.println("Background task is running."); } }, 0, 1, TimeUnit.SECONDS); executor.setExecuteExistingDelayedTasksAfterShutdownPolicy(false); // 确保shutdown后不再执行delayed任务 executor.setKeepAliveTime(10, TimeUnit.SECONDS); // 设置线程空闲超时时间 executor.allowCoreThreadTimeOut(true); // 允许核心线程超时关闭 // 等待5秒钟,然后取消任务 try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } future.cancel(true); executor.shutdown(); } }
四、daemonthread使用的注意事项
1.不要将需要确保完成的操作放在守护线程中执行
因为守护线程会随着JVM一起关闭,如果需要确保任务完成,不能使用守护线程。
2.不要在守护线程中使用sleep()方法
因为当JVM退出时,守护线程可能会被强制中断,在sleep()期间中断会抛出InterruptedException异常。
3.不要让守护线程持有任何锁
因为守护线程会在非守护线程结束时自动结束,如果它持有锁,那么其他线程就无法再获得该锁,这会造成死锁。
4.守护线程无法在main方法结束后继续执行
因为当main方法结束时,JVM会尝试停止所有线程。如果所有的非守护线程都结束了,守护线程也会被停止。
5.需要使用内存屏障来同步数据访问
因为守护线程的优先级低,因此在数据访问时很可能会出现并发问题。可以使用内存屏障来同步数据访问,保证代码的正确性。
原创文章,作者:小蓝,如若转载,请注明出处:https://www.506064.com/n/246752.html