JAVA-初步认识-第十二章-JVM中的多线程分析
一.
我们学java程序学到现在,到底是多线程的,还是单线程的?(现实中多线程是一直存在的,我们只是才开始学习这个概念而已,没学过不代表不存在。)
虚拟机的启动本身就依赖了多条线程,为什么?
举例来看,(举实例来研究多线程)
New了三个匿名对象,画一个堆内存演示一下。
左边是栈,右边是堆。栈里面有主函数,主函数里有代码。堆里面开始创建对象,匿名对象一创建完就是垃圾了(没有指向),现在有三个垃圾。
主函数里面代码有很多,它继续在执行。
从这里面立刻就可以得到第一点,就是我们的java虚拟机在运行的时候肯定有一条线程是在执行主函数的。这是必须的,没有这个线程,主函数根本无法运行。有人说,不是虚拟机调用主函数的么?再怎么调用,都要依赖线程来执行,因为只有线程才能执行代码。其中有一条线程是负责主线程执行的。(调用函数完全是编程语言,线程则有点涉及硬件了)
在堆内存中出现了垃圾,需要回收。如果出现了回收的动作,有人说主线程来执行垃圾的回收,那也就意味着程序运行到一半停止了,主函数去执行垃圾的回收了。这不对,垃圾的回收我们是看不着的。
主线程执行的过程中,垃圾回收器会不定时地来回收这个垃圾,意味着有人在做这个回收的动作,这就代表着有两个线程。其实不止两个,有很多线程,没有必要一一介绍。虚拟机底层它有开启多线程需要去做的任务,只不过这条任务我们可以分析地出来(这条任务就是主函数运行,垃圾回收)。→虚拟机在程序运行过程中貌似很重要。
本身虚拟机的启动,线程就不止一个。专门处理垃圾回收的线程我们称之为垃圾回收线程,在主函数中执行代码的线程称之为主线程。
任何一个线程启动后,都有它们自己的任务。(线程任务会不会用代码块封装起来?)
垃圾回收线程的内容,或者说任务代码都在垃圾回收器内部定义呢,我们不用管,它本身是在内部自动运行的,不需要我们去执行它。
对于垃圾回收装置,我们做一个简单地演示,是怎么回收的。这是底层内部完成的。
怎么演示呢?试想一下,堆内存中会产生很多对象,每个对象都具备着被回收的可能。对象怎么被回收的,只有对象自己最清楚。对象创建完以后,每个对象都有自己的执行特点,它怎么被回收,它自己最清楚,每个对象都具备着被回收的方法。写对象的时候,调用这个方法么?没有,每个对象都具备的方法在object类中。
这个方法就是finalize,如果不需要配置系统资源或执行其他清除,就不用重写finalize方法。(finalize就是回收)
现在验证一下,finalize方法到底是怎么一回事。
其实根本不用复写,因为它不需要做这个事情,它里面产生对象并没有做太多封装数据,直接把内存清除掉就完事了,跟我们栈内存释放原理,清除内存空间把数据抹去,存储其它数据就完事了。只有涉及到系统资源的,才会去进行finalize的定义以及关闭系统资源。这里写finalize,只是添加了一个输出语句,没有改变系统固有finalize的功能,也是为了展现垃圾回收线程的存在。
(如果我们这里不覆写finalize的话,对象本身是有finalize方法的,继承自object类。finalize方法中原有的内容是系统给书写好的,这里不覆写的话,看不出来finalize方法执行过,估计原有的finalize方法中内容在执行时不会打印在DOS控制台上。)
DOS的运行结果没有demo ok。→这表明了除了主线程外,还有其他线程的存在。而且这个线程在主线程执行完了,还没开始操作。既然写了语句,必然要执行的,没出现就表明还没执行,不是主线程执行的。finalize()方法可以主动执行么,调用它?
我们知道这三个匿名对象是变成垃圾了,但是它们立刻就被回收了吗?不一定,它们在不定时的时间里回收。
(finalize方法的修饰符是protected void)
什么是垃圾回收器呢?就是垃圾回收的那个程序。这程序在做垃圾回收的动作,这程序在被单独的一个线程负责执行。如何找到这个垃圾回收器。(我猜是众多垃圾对象对应一个垃圾回收器和一个垃圾回收线程)
系统类里面有一个gc方法。这个方法可以运行垃圾回收器。通过这个系统类的静态方法来运行垃圾回收器。
截图中的gc不是立刻让垃圾回收器启动,是在告诉垃圾回收器你要启动,至于怎么启动它说了算的。什么时候操作垃圾回收线程也是不确定的。
为什么hello world先输出了?按理说,垃圾回收器启动了,它会回收两个垃圾吧,一收的时候就调用了这个对象的finalize方法。说了嘛,垃圾回收器会调用finalize方法,一打印说明有垃圾被回收掉了。
(gc方法启动垃圾回收器,垃圾回收器启用finalize方法,真正起到回收作用的是finalize→这个和start和run的关系有点像,管启动,和管运行的不是一个。)
为什么hello world会在demo ok前面打印呢?因为这是两个线程完成的。主线程调用到gc的时候,这个时候就在告诉垃圾回收器。垃圾回收器就会在不定时启动机器,同时主线程继续往下运行,而垃圾回收线程独立于主线程操作。在主线程打印了hello world后,垃圾处理器才处理了两个垃圾对象。
修改一下程序,再看结果
(gc方法是直属于系统的,用来的启动垃圾回收线程的。直接用System.gc()的方式就可以,系统直接调用。这样的书写方式显示垃圾回收线程貌似只有一个,不然肯定要表明属于谁,谁调用。)
编译运行多次后,出现了两种DOS结果。这里面两个概念,一个是slime,一个是优先级。现在先不谈技术,先讨论一下现象。
除了上面两种情况外,还有可能出现demo ok先出来的情况。
上图的这种情况是,垃圾回收器刚回收了一个,虚拟机就结束了。虚拟机结束的话,它会强制地结束它所在的进程(就是内存区域),就是java.exe程序被结束进程了,内存就都被清除了。因此,还没等调用finalize就结束了。(整个虚拟机都结束了,更何况里面的垃圾回收线程)→这就说之前有一个地方我们理解错了,没有gc()方法存在时,demo ok没出现的原因,除了垃圾线程启动慢之外,还有虚拟机结束了进程,也就是将该程序结束了,那就不存在垃圾对象的事情了。
上面的例子验证出来,至少有两个线程。
其实还有一个线程,主线程执行完以后,这些代码都结束了。线程的任务只要一结束,线程就结束了,也就意味着主线程结束了。有人说主线程都结束了,虚拟机还搞什么?hello world都打印完了,虚拟机应该退出。告诉你,仅仅是主线程结束了,虚拟机中的其他线程还在执行,虚拟机还没结束呢。→该程序中出现的垃圾对象,会随着主线程的结束,而放弃垃圾线程么?
总结:虚拟机当中所涉及的多线程内容。