文档章节

Java中的父线程与子线程

Hosee
 Hosee
发布于 2015/09/22 12:48
字数 1376
阅读 1.2W
收藏 11

以前在学习操作系统的时候,一直记得的父线程死后,子线程也消失了。然而今天在查资料中,发现有点疑惑,在此记录一下。

Java编写的程序都运行在Java虚拟机(JVM)中,在JVM的内部,程序的多任务是通过线程来实现的。

每用java命令启动一个java应用程序,就会启动一个JVM进程。在同一个JVM进程中,有且只有一个进程,就是它自己。在这个JVM环境中,所有程序代码的运行都是以线程来运行的。JVM找到程序的入口点main(),然后运行main()方法,这样就产生了一个线程,这个线程称之为主线程。当main方法结束后(没有其他线程时),主线程运行完成。JVM进程也随即退出。

操作系统将进程线程进行管理,轮流(没有固定的顺序)分配每个进程很短的一段时间(不一定是均分),然后在每个进程内部,程序代码自己处理该进程内部线程的时间分配,多个线程之间相互的切换去执行,这个切换时间也是非常短的。

对于程序来说,如果主进程在子进程还未结束时就已经退出,那么Linux内核会将子进程的父进程ID改为1(也就是init进程),当子进程结束后会由init进程来回收该子进程。

那如果是把进程换成线程的话,会怎么样呢?假设主线程在子线程结束前就已经退出,子线程会发生什么?

首先我们来看一个网上很多人的例子:

package test;

public class Test1 extends Thread
{
	@Override
	public void run()
	{
		while (true)
		{
			try
			{
				Thread.sleep(2000);
			}
			catch (InterruptedException e)
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println("我还活着");
		}
	}

	public static void main(String[] args) throws InterruptedException
	{
		Thread t = new Test1();
		t.start();
		Thread.sleep(5000);
		System.out.println("Main End");
	}
}



输出:

我还活着
我还活着
Main End
我还活着
我还活着



上文说了 JVM找到程序的入口点main(),然后运行main()方法,这样就产生了一个线程,这个线程称之为主线程。当main方法结束后(没有其他线程时),主线程运行完成。JVM进程也随即退出。然而上述输出表明当main()运行到最后后,子线程依然在输出。所以大家就得出了结论,父线程要等待子线程完成后才会退出。然而我们再看个例子:

package test;

public class Test extends Thread
{
	@Override
	public void run()
	{
		Thread sonthread = new a();
		sonthread.start();
	}

	public static void main(String[] args) throws InterruptedException
	{
		Thread fatherThread = new Test();
		fatherThread.start();
		Thread.sleep(5000);
		fatherThread.interrupt();
		Thread.sleep(2000);
		System.out.println("fatherThread.isAlive()?  "+fatherThread.isAlive());
	}
}

class a extends Thread
{
	@Override
	public void run()
	{
		while (true)
		{
			try
			{
				Thread.sleep(1000);
			}
			catch (InterruptedException e)
			{
				e.printStackTrace();
			}
			System.out.println("我还活着");
		}
	}
}



输出:

我还活着
我还活着
我还活着
我还活着
我还活着
我还活着
fatherThread.isAlive()?  false
我还活着
我还活着
我还活着



很明显,父线程死后子线程还在输出。两个例子到底哪个是正确的呢?

查了很多资料得到了解答。

如果main方法中没有创建其他线程,那么当main方法返回时JVM就会结束Java应用程序。但如果main方法中创建了其他线程,那么JVM就要在主线程和其他线程之间轮流切换,保证每个线程都有机会使用CPU资源,main方法返回(主线程结束)JVM也不会结束要一直等到该程序所有线程全部结束才结束Java程序(另外一种情况是:程序中调用了Runtime类的exit方法,并且安全管理器允许退出操作发生。这时JVM也会结束该程序)。

那么又有个思考,JVM是怎么知道线程都结束的呢?

JVM中有一个线程DestroyJavaVM,执行main()的线程在main执行完后调用JNI中的jni_DestroyJavaVM()方法唤起DestroyJavaVM线程。JVM在Jboss服务器启动之后,就会唤起DestroyJavaVM线程,处于等待状态,等待其它线程(java线程和native线程)退出时通知它卸载JVM。线程退出时,都会判断自己当前是否是整个JVM中最后一个非deamon线程,如果是,则通知DestroyJavaVM线程卸载JVM。ps:扩展一下:1.如果线程退出时判断自己不为最后一个非deamon线程,那么调用thread->exit(false),并在其中抛出thread_end事件,jvm不退出。2.如果线程退出时判断自己为最后一个非deamon线程,那么调用before_exit()方法,抛出两个事件: 事件1:thread_end线程结束事件、事件2:VM的death事件。然后调用thread->exit(true)方法,接下来把线程从active list卸下,删除线程等等一系列工作执行完成后,则通知正在等待的DestroyJavaVM线程执行卸载JVM操作。

所以第一个例子时,主线程运行完,但是它不是最后一个非守护线程,所以JVM并没有退出,所以子线程还会继续运行。

第二个例子。主线程一直在,所以JVM不会退出。当父线程死去后,子线程还在运行。说明父线程的生命周期与子线程没有关系。

参考资料:

1. http://warnerhit.iteye.com/blog/1407484

2. http://liyuanlife.com/blog/2015/04/08/influence-of-main-threads-exiting-to-child-thread/

3. http://blog.yemou.net/article/query/info/tytfjhfascvhzxcytp47

4. http://jinguo.iteye.com/blog/747256

© 著作权归作者所有

Hosee
粉丝 621
博文 135
码字总数 209956
作品 0
杭州
程序员
私信 提问
加载中

评论(0)

ThreadLocal与InheritableThreadLocal区别

最近项目中为了让打印的日志可以追踪,看到了Logback和log4j2支持的MDC功能,其内部利用了子线程从父线程继承InheritableThreadLocal类型变量的特性。以前只使用到了ThreadLocal,在这里做一...

特拉仔
2018/09/01
890
0
InheritableThreadLocal详解

1、简介 在上一篇 ThreadLocal详解 中,我们详细介绍了ThreadLocal原理及设计,从源码层面上分析了ThreadLocal。但由于ThreadLocal设计之初就是为了绑定当前线程,如果希望当前线程的ThreadL...

沈渊
2018/04/12
0
0
ThreadLocal和InheritableThreadLocal深入分析

  通过ThreadLocal和InheritableThreadLocal,我们能够很方便的设计出线程安全的类。JDK底层是如何做到的呢?ThreadLocal和InheritableThreadLocal有什么区别呢与联系呢?为什么有了Threa...

良辰美景TT
2018/08/14
0
0
Java并发编程之ThreadLocal源码分析

多线程的线程安全问题是微妙而且出乎意料的,因为在没有进行适当同步的情况下多线程中各个操作的顺序是不可预期的,多线程访问同一个共享变量特别容易出现并发问题,特别是多个线程需要对一个...

狂小白
2018/06/03
0
0
ThreadLocal 类 的源码解析以及使用原理

1、原理图说明      首先看这一张图,我们可以看出,每一个Thread类中都存在一个属性 ThreadLocalMap 成员,该成员是一个map数据结构,map中是一个Entry的数组,存在entry实体,该实体包...

小勇DW3
2018/08/13
0
0

没有更多内容

加载失败,请刷新页面

加载更多

四个步骤,学会单片机

有很多想学习单片机的朋友,但是不知道怎么入门,今天来讲讲我学习单片机的一些感受以及方法。由于单片机是一门要求动手的技术,所以,建议先确定所要学习单片机的型号,然后选用一块开发板,...

demyar
16分钟前
19
0
Nginx 配置获取用户真实IP

server { listen 80; server_name baidu.com; location / { if ($http_user_agent !~* (mobile|nokia|iphone|ipad|android|samsung......

牛A和牛C之间的我
18分钟前
13
0
Tower for Mac(强大的Git客户端) v4.3

Tower mac版是Macos上一款强大的Git客户端,具有强大的Git资源库管理、版本控制、分支管理等等,已被很多知名公司使用,并且能够和Xcode、GitHub、Beanstalk、BBEdit等软件无缝结合使用 . 功...

麦克W
19分钟前
19
0
Android知识体系总结2020之Android部分网路编程篇

1.计算机网络基础 计算机网络基础 2.网络数据解析 网路数据解析篇 3.Android网络编程 HttpClient和HttpURLConnection【比较过时,了解即可】 OkHttp Retrofit...

ClAndEllen
19分钟前
11
0
leaflet常用插件

1、常用地图切换加载(osm、google、baidu、gaode、tianditu.etc) https://github.com/htoooth/Leaflet.ChineseTmsProviders 2、切片地图加载(wmts)(支持矢量切片) https://github.com...

东东笔记
20分钟前
22
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部