个人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打印的结果中的其他线程感兴趣,可以参考
这篇文章。
分享到:
相关推荐
经常会遇到下述问题:很多io busy的应用采取多线程的方式来解决,但这时候会发现python命令行不响应ctrl-c 了,而对应的java代码则没有问题: 复制代码 代码如下: public class Test { public static void main...
invokeMethod.java 同一个类中调用方法示例 invokeOther.java 类的外部调用方法示例 invokeStaticMethod.java 调用静态方法示例 localVariable.java 演示局部变量 localVSmember.java 局部变量与成员变量同名...
与cgi的区别在于servlet处于服务器进程中,它通过多线程方式运行其service方法,一个实例可以服务于多个请求,并且其实例一般不会销毁,而CGI对每个请求都产生新的进程,服务完成后就销毁,所以效率上低于servlet。...
一款用Java做的象棋游戏 import java.awt.*; import java.awt.event.*; import javax.swing.*; import java.util.*; import java.io.*; //主类 public class Chess{ public static void main(String args[]){ ...
与cgi的区别在于servlet处于服务器进程中,它通过多线程方式运行其service方法,一个实例可以服务于多个请求,并且其实例一般不会销毁,而CGI对每个请求都产生新的进程,服务完成后就销毁,所以效率上低于servlet。...
import java.awt.*; import java.awt.event.*; import javax.swing.*; import java.util.*; import java.io.*; //主类 public class Chess{ public static void main(String args[]){ new ChessMainFrame("中国...
如果返回的下一个时间早于当前时间,则退出 */ @Override public long RunInCurrentThread(long nCurrentMilliSecond) { return nCurrentMilliSecond+=nDelay; } } //5·创建主线程代码 public class ...
其中a为1至9之中的一个数,项数也要可以指定。 import java.util.Scanner; class Multinomial{ public static void main(String[] args){ int a; //定义输入的 a int howMany; //定义最后的一项有多少个数字 ...
//把第一次的单击棋子给线程响应 static int Man,i; ChessMainFrame(){} ChessMainFrame(String Title) { //获行客格引用 con = this.getContentPane(); con.setLayout(null); //实例化规则类 ...
D) Java语言中的多态的含义可以表达为:对外一个接口,内部多种实现。Java语言支持两种多态:运行时多态和编译时多态。 题目8:a 程序如下: public class Demo extends Base { private int count; public Demo()...
一个轻量级的 Java 调试器,基于它扩展了 . 它允许用户使用 Visual Studio Code (VS Code) 调试 Java 代码。 以下是功能列表: 启动/附加 断点/条件断点/日志点 例外 暂停并继续 步入/退出/结束 变量 调用栈 线程 ...
1.3一个简单的Java应用程序14 1.4一个简单的Java小程序16 1.5本章小结18 第2章Java语言基础19 2.1Java语言的特点19 2.2Java程序的构成21 2.3数据类 型23 2.3.1基本数据类型23 2.3.2常量25 2.3.3变量26 ...
public static JFrame main=new JFrame("JAVA端口扫描器"); //显示扫描结果 public static JTextArea Result=new JTextArea("",4,40); //滚动条面板 public static JScrollPane resultPane = new JScrollPane...
因此Java同样提供了一个Socket对象来对其进行支持,只要客户方创建一个Socket的实例对象进行支持就可以了。Socket client=new Socket(InetAddress.getLocalHost(),5678);客户机必须知道有关服务器的IP地址,对于着...
1)为每个读者/写者产生一个线程,设计正确的同步算法 2)每个读者/写者对该存储区进行操作后,即时显示该存储区的全部内容、当前指针位置和读者/写者线程的自定义标识符。。 3)读者应有3个以上,写者应有有两个...
* 3、启动客户端线程,监视服务器是否转发消息,然后显示到界面 * 4、直接通过自己的socket发送消息 * */ public void actionPerformed(ActionEvent e) { if (e.getSource() == connectbutton) { String...
2、编写一个类,在main方法中定义一个Map对象(采用泛型),加入若干个对象,然后遍历并打印出各元素的key和value。 3、取出一个字符串中字母出现的次数。如:字符串:"abcdekka27qoq" ,输出格式为:a(2)b(1)...
NO.3 "2.3设计思路与具体实现方法 " "2.3.1产生一个窗口 " "(1)定义一个框架,确定大小。其大小使600,400; " "(2)定义一个贪吃蛇的类,确定其大小比例; " "(3)在窗口上方定义一个Score得分; " "(4) 在窗口的下方...
run()方法可以产生必须退出的标志来停止一个线程。 40.接口是否可继承接口? 抽象类是否可实现(implements)接口? 抽象类是否可继承实体类(concrete class)? 答:接口可以继承接口。抽象类可以实现(implements)接口...
/* 启动一个服务线程,为客户端服务 */ new ServerThread(s).start(); } catch (IOException e) { e.printStackTrace(); } } } /** * 成员内部类,服务线程类 */ class ServerThread extends Thread { ...