个人blog原文地址:
http://www.gemoji.me/when_main_thread_end/
之所以写这篇文章,是因为上次被人问到这么一个问题:“在main函数里启动一个定时器,是不是main函数执行完整个程序就退出了,包括那个定时器”。多说无益,直接写个程序测试一下就知道了。
public class MainThreadTest {
public static void main(String[] args) {
new Timer().schedule(new TimerTask(){
@Override
public void run() {
System.out.println("Timer thread is running...");
}
}, 500, 500);
System.out.println("Main thread ends!");
}
}
然后你就会发现执行结果是这样的:
引用
Main thread ends!
Timer thread is running...
Timer thread is running...
Timer thread is running...
这至少说明main函数执行完毕之后,定时器依旧在运行,那么main线程是否退出了呢?我想不少同学可能会和我之前一样脑补这样的逻辑:“timer线程是main线程创建的,main线程会等到自己创建的所有线程都退出后才会退出”,现实当中我们有时真的会被一些自己妄自推断的结论给弄得雨里雾里,最好的做法是在自己产生一个想法之后想办法去证明它或者证否它。回到这个例子,实际上,当前有哪些线程在运行,我们是可以看到的。怎么看?用java自带的工具jstack就能看到,jstack的使用方法就不多说了,请自行搜索。通过jstack我们可以把jvm当前的线程状态dump下来,在dump结果里你会看到很多陌生的线程,这都没关系,你只需要关注下面的两个线程就好。
引用
"Timer-0" prio=6 tid=0x01b8f800 nid=0x1330 in Object.wait() [0x0c10f000..0x0c10f
be8]
java.lang.Thread.State: TIMED_WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x03bcdd18> (a java.util.TaskQueue)
at java.util.TimerThread.mainLoop(Timer.java:509)
- locked <0x03bcdd18> (a java.util.TaskQueue)
at java.util.TimerThread.run(Timer.java:462)
"main" prio=6 tid=0x01989400 nid=0x4b8 at breakpoint[0x003bf000..0x003bfe24]
java.lang.Thread.State: RUNNABLE
at me.gemoji.www.test.MainThreadTest.main(MainThreadTest.java:18)
上面的线程状态是我在main函数中加断点后运行看到的结果,你会看到这时候main线程肯定是还存在的,而且可以明确地看到它处于”at breakpoint”的状态。那么要是我让main函数继续执行下去呢?你会发现main线程不见了,而定时器的线程依旧在运行,这就充分证明main线程在main函数执行完之后就退出了,并不是最后一个退出的线程。
上面是通过实践得出的结论,下面补充一下理论。stackoverflow上有个类似的问题:
When does the main thread stop in Java?,把里面的答案总结一下,基本上就这么三句话:
- JVM会在所有的非守护线程(用户线程)执行完毕后退出;
- main线程是用户线程;
- 仅有main线程一个用户线程执行完毕,不能决定JVM是否退出,也即是说main线程并不一定是最后一个退出的线程。
关于守护线程的相关知识,大家可以自行搜索下,另外,如果你对jstack打印的结果中的其他线程感兴趣,可以参考
这篇文章。
分享到:
相关推荐
每个Java程序都有一个主线程,即由JVM启动并执行main方法的线程。线程代表了程序中的执行流,可以在不同的线程之间切换以共享CPU时间。线程的状态包括新建、运行、中断和死亡。线程的生命周期始于新建,通过调用...
默认情况下,Java程序的main方法在一个称为主线程的线程中运行。当main方法执行完毕,主线程结束,JVM进程也会随之退出。 Java提供了两种创建线程的方式:一是继承自`java.lang.Thread`类,二是实现`java.lang....
在 main 方法中,我们创建了一个 TestThread 对象,并启动了该线程。然后,在 while 循环中,我们输出当前线程的名称和状态。 第二个例子:前台线程和后台线程 在 Java 中,有两种类型的线程:前台线程(用户线程...
在 Java 中,线程中断是通过 interrupt 方法来实现的,该方法只是给线程置一个中断标志位,并不会立即停止线程的运行。interrupted 方法可以判断是否被中断,静态方法 interrupted() 则可以判断是否被中断,并清除...
此时,线程尚未启动,只是内存中存在了一个线程对象。 2. 就绪状态(Runnable):调用Thread对象的start()方法后,线程进入就绪状态,表示线程准备运行,但不保证立即执行,需要等待CPU调度。 3. 运行状态...
Java多线程是Java编程中一个重要的概念,它允许程序同时执行多个任务,极大地提高了程序的效率和响应性。在Java中,多线程主要分为两种实现方式:通过子类化Thread类和实现Runnable接口。 1. 子类化Thread类: 当...
我们可以通过`java.util.Stack`类来实现栈,或者自定义一个栈类,包含`push()`和`pop()`方法,以及检查栈是否为空或满的方法。 同步实现的关键在于,当仓库满时,生产者必须等待,直到消费者消费了一些产品;同样,...
一个进程中可以包含多个线程。如java.exe进程中可运行多个线程。 - 线程总是隶属于某个进程,同一进程内的多个线程共享内存空间。 2. **Java中的线程** - 在Java中,“线程”有两层含义: - `java.lang.Thread` ...
本文将深入探讨Java多线程模型的相关知识点,包括线程与进程的区别、线程的实现原理、线程的创建方法以及线程的阻塞与唤醒机制等,旨在为初学者提供一个清晰的多线程概念理解和使用指南。 一、线程与进程的区别 在...
Java多线程机制是编程中一个重要的概念,它允许程序同时执行多个任务,提升程序的效率和响应性。在Java中,线程是程序执行的基本单元,比进程更细粒度,一个进程可以包含多个线程。每个线程有自己的生命周期,包括...
该段代码展示了Java中的一个简单的单线程程序,通过`SequentialDemo`类来实现。在这个例子中,有两个`Sequential`对象被创建并调用了它们的`run()`方法。 ```java public class SequentialDemo { public static ...
Java多线程是Java编程中的一个重要概念,它允许程序同时执行多个任务,从而提高系统效率和资源利用率。在Java中,实现多线程有两种主要方式:通过实现`Runnable`接口或者继承`Thread`类。 一、实现Runnable接口 ...
本文将深入探讨Java线程停止的方法,特别是通过一个示例代码片段来解析如何利用标志变量(Flag)控制线程的生命周期,以及这种方法背后的原理与最佳实践。 ### Java线程停止方法概述 在Java中,直接调用线程的`...
每个Java程序至少包含一个主线程,在程序启动时由JVM创建并调用`main()`方法。 - **资源共享与隔离**:虽然线程共享进程的内存空间,但每个线程有自己的堆栈、程序计数器和局部变量。这意味着线程间可以轻松共享信息...
// 中断线程,通常用于通知线程退出循环 } } ``` 在用户级线程调度中,应用程序负责管理和调度线程的执行。这意味着当一个线程阻塞(如等待I/O操作)时,程序需要决定哪个线程应该获得CPU资源。在单个CPU上,这...
- **主线程**:Java应用程序从main()方法开始,main()方法运行在一个线程中,即为主线程。 - **用户线程**和**守护线程**:用户线程是程序执行的主要部分,所有用户线程结束后,JVM才会退出。守护线程(如垃圾收集...
通过`new Thread`创建线程对象后,使用`start()`方法启动线程,而不是直接调用`run()`,因为`start()`会启动一个新的执行线程并调用`run()`,而直接调用`run()`只会在一个已存在的线程中执行,不会产生并发效果。...
每个进程至少有一个线程,而一个进程可以拥有多个线程。这些线程可以共享进程的内存空间,因此能够高效地交换数据。 #### 三、创建和启动线程的方法 创建线程主要有三种方法:继承`Thread`类、实现`Runnable`接口...
Java虚拟机(JVM)中,每个Java应用都会启动一个JVM进程,而线程则是程序执行的实体。Java中的线程可以通过两种方式创建:继承Thread类或实现Runnable接口。主线程通常由main方法启动,当主线程执行完毕,JVM进程也...
守护线程和用户线程的主要区别在于,当最后一个非守护线程结束时,JVM会正常退出,而不管当前是否有守护线程。 守护线程的创建可以通过设置线程的daemon参数为true来实现。例如: ```java public static void main...