线程是JVM调度单位,进程是OS资源容器;Java程序启动即创建独立JVM进程,其内线程共享堆与方法区,但栈、计数器等私有;Thread.sleep()不释放锁,Object.wait()会释放monitor锁并入等待队列;ProcessBuilder启动的是隔离新进程,无法访问父JVM状态;非守护线程崩溃通常不终止JVM,但Error或System.exit()会。

线程是JVM内部的调度单位,进程是操作系统分配资源的容器
Java程序一启动,就诞生一个JVM进程(比如执行 java -jar app.jar),它拥有独立的堆内存、文件句柄、PID等系统资源;而所有Java线程(包括 main、GC线程、自定义线程)都运行在这个进程内部,共享堆和方法区,但各自有私有的 虚拟机栈、程序计数器 和 本地方法栈。
这意味着:你无法用纯Java代码“创建新进程”来共享JVM堆——Runtime.exec() 或 ProcessBuilder 启动的是全新进程,和当前JVM完全隔离,连类加载器、静态变量、堆对象都不互通。
为什么Thread.sleep()不释放锁,而Object.wait()会?这和进程无关,只在线程层面生效
这个问题常被误认为和“进程阻塞”有关,其实完全发生在单个JVM进程内的线程调度层:
-
Thread.sleep()是线程主动让出CPU时间片,但**持有锁不放**——因为没进入等待队列,只是暂停执行 -
Object.wait()必须在同步块中调用,它会让当前线程释放持有的monitor锁,并进入该对象的等待队列 - 两者都不涉及进程切换,操作系统甚至不知道你在sleep还是wait;JVM线程调度器在用户态完成状态转换
用ProcessBuilder启动子进程时,别指望它能访问父JVM的变量或Spring Bean
这是新手高频误解:以为 ProcessBuilder 启动的也是“Java线程”,结果发现子进程里读不到配置、连不上数据库。
立即学习“Java免费学习笔记(深入)”;
真实情况是:
- 子进程是全新JVM实例,从头加载类、初始化静态字段、重建Spring容器
- 父子进程通信只能靠外部通道:
stdin/stdout、临时文件、Socket、Redis等 - 如果真要共享状态,应改用线程池 +
ConcurrentHashMap或AtomicInteger,而不是硬拉起新进程
ProcessBuilder pb = new ProcessBuilder("java", "-cp", "lib/*", "com.example.SubApp");
pb.inheritIO(); // 只继承IO流,不继承内存、线程、类加载器
Process p = pb.start();
线程崩溃会导致整个JVM进程退出吗?不一定,但多数情况会
Java线程默认是非守护线程(isDaemon()==false),只要还有一个非守护线程在跑,JVM就不会退出。但崩溃行为取决于异常类型:
- 未捕获的
RuntimeException(如NullPointerException)只会终止该线程,不影响其他线程(除非它是最后一个非守护线程) - 若在线程中抛出
Error(如OutOfMemoryError、StackOverflowError),JVM可能直接挂掉——因为堆或栈已不可信 -
System.exit()会立刻终止整个JVM进程,不管有多少线程在跑
所以线上服务必须对关键线程加 try-catch(Throwable),并记录日志;不能假设“一个线程崩了,别的还能扛”。










