前情提要
写这篇随笔的原因是最近在写 mit 6.824的 lab1,实验中使用 rpc 作为 coordinator 和 worker 的通信方式。因为之前一直使用 Java,所以就想对比一下两种语言的退出机制,也算是对 Java 理解的巩固。
Java 机制
Java 有两种线程:
- 非守护线程(用户线程):比如 main 方法就是一个非守护线程
- 守护线程:服务于其他线程的线程(这个思想很重要,后面会再提到),不需要上层逻辑介入。如果其他非守护线程都已经结束,守护线程没有了服务对象,那么守护线程也就退出了。
- 我理解的“不需要上层逻辑介入”:其实就是守护线程根本不关心创建它的上层线程具体做什么,虽然依赖上层线程,但是守护线程只关心自己做什么,也不需要上层线程介入。比如后面提到的“GC 垃圾回收线程”。
用户线程和守护线程的区别:
- 在一个 JVM 实例中,当所有的用户线程运行结束后,JVM 退出,当前的进程结束(如果调用了 System.exit()方法,即使还有非守护线程正在执行,那么 JVM 也会退出)
- 如果一个线程是守护线程,即使该线程没有运行结束,但是所有的用户线程已经运行完,JVM 仍旧会退出。
如何设置守护线程:
调用 Thread 的 setDaemon() 方法,这个方法默认是false:
public class TestThread {
public static void main(String[] args) {
MyThread myThread = new MyThread();
Thread thread = new Thread(myThread);
thread.setDaemon(true);
thread.start();
System.out.println("world");
}
static class MyThread implements Runnable {
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("hello");
}
}
}
在上面的 demo 中,MyThread 被设置为守护线程,那么 main 方法输出 “world” 之后整个程序就退出了,而不会继续打印 “hello”。
如果不设置为守护线程,那么上面的 demo 会正常地输出 hello,在 sleep 5 秒之后。
守护线程的使用场景
- GC 垃圾回收线程:垃圾回收线程就是用于回收其他用户线程产生的垃圾,垃圾回收线程就是服务于其他用户线程。如果所有的用户线程都运行结束,那么垃圾回收线程也就没有存在的必要了。
注意事项
守护线程不能持有任何需要关闭的资源,例如打开文件等,因为虚拟机退出时,守护线程没有任何机会来关闭文件,这会导致数据丢失。
go 机制
在 go 语言中,一定要注意的是, goroutine 以非阻塞的方式执行,它们会随着程序(main goroutine)的结束而消亡。也就是说,当main 函数执行结束后,那么所有由 main 函数产生的 goroutine 也会结束,即使 goroutine 没有执行完。
如果一个 goroutine B 不是由 main 函数产生,而是由另外一个 goroutine A 产生。在 main 函数没有执行结束的前提下,那么即使 A 结束了,B 也不会结束。
所以,在 go 中,如果想要等待所有 goroutine 执行结束再退出,就需要使用其他手段了,比如sync 包中提供了 WaitGroup 类型。
标签:Java,goroutine,程序,线程,Go,结束,main,守护 From: https://www.cnblogs.com/supportmyself/p/15648757.html