• java并发编程 守护线程 用户线程 main


    目录

    守护线程无循环逻辑

    守护线程有循环逻辑

    用户线程无循环逻辑

    用户线程有循环逻辑

    结论

    疑问

    ThreadPoolExecutor

    CompletableFuture


    经常使用线程,没有对守护线程和用户线程的区别做彻底了解

    下面写4个例子来验证一下

    源码如下

    1. /* Whether or not the thread is a daemon thread. */
    2. private boolean daemon = false;
    3. /**
    4. * Marks this thread as either a {@linkplain #isDaemon daemon} thread
    5. * or a user thread. The Java Virtual Machine exits when the only
    6. * threads running are all daemon threads.
    7. *
    8. *

      This method must be invoked before the thread is started.

    9. *
    10. * @param on
    11. * if {@code true}, marks this thread as a daemon thread
    12. *
    13. * @throws IllegalThreadStateException
    14. * if this thread is {@linkplain #isAlive alive}
    15. *
    16. * @throws SecurityException
    17. * if {@link #checkAccess} determines that the current
    18. * thread cannot modify this thread
    19. */
    20. public final void setDaemon(boolean on) {
    21. checkAccess();
    22. if (isAlive()) {
    23. throw new IllegalThreadStateException();
    24. }
    25. daemon = on;
    26. }

    看注释的含义是,daemon 为 true 时是守护线程,false 为用户线程。当所有守护线程在执行时 jvm 会退出。

    守护线程无循环逻辑

    1. public class DaemonThreadTest {
    2. public static void main(String[] args) {
    3. Thread thread = new Thread(() -> {
    4. System.out.println("这是一个守护线程");
    5. });
    6. // 设置守护线程
    7. thread.setDaemon(true);
    8. thread.start();
    9. }
    10. }

    执行结果无内容,说明 main 在执行完后就结束了,子线程没来得及执行。

    守护线程有循环逻辑

    1. public class DaemonThreadCycleTest {
    2. public static void main(String[] args) {
    3. Thread thread = new Thread(() -> {
    4. while (true) {
    5. }
    6. });
    7. // 设置守护线程
    8. thread.setDaemon(true);
    9. thread.start();
    10. System.out.println("主线程测试守护线程结束");
    11. }
    12. }

    执行结果

    主线程测试守护线程结束

    同上

    用户线程无循环逻辑

    1. public class UserThreadTest {
    2. public static void main(String[] args) {
    3. Thread thread = new Thread(() -> {
    4. System.out.println("这是一个用户线程");
    5. });
    6. thread.start();
    7. }
    8. }

    执行结果

    这是一个用户线程

    主线程结束后,子线程执行了。

    用户线程有循环逻辑

    1. public class UserThreadCycleTest {
    2. public static void main(String[] args) {
    3. Thread thread = new Thread(() -> {
    4. while (true) {
    5. }
    6. });
    7. thread.start();
    8. System.out.println("主线程测试用户线程结束");
    9. }
    10. }

    执行结果

    主线程测试用户线程结束

    此时,idea 显示程序执行还未结束,查看 jps 信息如下

    1. C:\Users\Rike>jps
    2. 27776
    3. 852 Jps
    4. 34568
    5. 29100 UserThreadCycleTest
    6. 29468 RemoteMavenServer
    7. 30684 Launcher

    说明主线程结束了,但是用户线程还在执行。

    结论

    守护线程的一个实现,jvm的垃圾回收,需要时创建,不需要时销毁。

    如果希望 main 线程在结束后子线程马上结束,在创建时显式设置为守护线程。

    如果希望 main 线程在结束后子线程继续执行,在创建时设置为用户线程或者不指定(默认为用户线程)。

    决定程序是否还执行的是用户线程,但是用户线程一直持续会有资源消耗的问题。因为jvm每个线程与cpu一一对应,线程存在会一直占用操作系统和机器资源。

    考虑到执行任务的问题,例如读取或者写入文件的时候,使用用户线程为好,但是需要设置退出策略。

    疑问

    java 的程序入口 main 方法所在的线程是用户线程还是守护线程?

    https://www.jianshu.com/p/91028dca187b

    个人猜测,main 线程是用户线程,如果是守护线程不符合逻辑,因为很多线程建立在它基础上,需要做很多关闭操作。看到这篇文章也是这样讲,但是没讲证据。正确性需要后面进行验证。

    考虑到了线程池 ThreadPoolExecutor 和异步执行器 CompletableFuture,看一下它们创建的线程是否有区别?

    ThreadPoolExecutor

    1. public ThreadPoolExecutor(int corePoolSize,
    2. int maximumPoolSize,
    3. long keepAliveTime,
    4. TimeUnit unit,
    5. BlockingQueue workQueue) {
    6. this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
    7. Executors.defaultThreadFactory(), defaultHandler);
    8. }

    如果 ThreadPoolExecutor 创建时 ThreadFactory 不指定,默认是 Executors.defaultThreadFactory(),可以自己指定。

    ThreadFactory  建议是自己指定,在实际项目执行时如果有多个线程池未进行指定会无法区别是哪个线程池在执行,出现问题也不好排查,指定了线程池名称,通过日志可以快速定位到问题。

    Executors

    可以看到,不管如何创建的线程一律是用户线程(既然强制这样设置,感觉这里的判断多余了,题外话),想到这里符合后台任务处理的情况,防止主程序退出了后台任务也结束了。

    CompletableFuture

    CompletableFuture 默认使用的线程池是 ForkJoinPool。

    经过断点调试,最终 ForkJoinWorkerThreadFactory 的实现类是 DefaultForkJoinWorkerThreadFactory

    通过 DefaultForkJoinWorkerThreadFactory 来创建线程

    CompletableFuture 最终创建的线程是守护线程。

    由此发现,ThreadPoolExecutor 和 CompletableFuture 创建的线程方式不一样。

    如果 ThreadPoolExecutor 是计算型非io任务的话,可以自定义线程工厂来处理使用守护线程这个问题。

    参考链接

    https://mp.weixin.qq.com/s/j_dwm-foKDTghMxwmdsz2w

    https://blog.csdn.net/MaYuKang/article/details/121931517

    https://blog.csdn.net/weixin_34268604/article/details/114863702

  • 相关阅读:
    “探索前后端分离架构下的Vue.js应用开发“
    linux等保整改
    EasyDSS视频直播点播平台出现断流该如何重连?具体步骤是什么?
    ACM. HJ24 合唱队 ●●
    SSM之spring注解式缓存redis
    海洋馆一日游
    java的Excel导出方式总结
    GBase 8a事务控制
    四年时间,从一个浑浑噩噩的程序员到csdn博客专家的成长之路
    【数据结构之排序】
  • 原文地址:https://blog.csdn.net/zlpzlpzyd/article/details/133500502