jvm学习(三)-java的进程和线程

         关于进程和线程的区别,相信每一个面试过的人都被问到过。

         进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位.

          线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。

          进程和线程的概念具体到java中,就是(个人理解,大神请喷):

         一个java程序的创建,就是一个进程,对应一个jvm虚拟机,一个java进程有一整套jvm内存模型(包括堆和栈---资源分配)。

         而java的栈,是在这个java进程的基础上进行资源的再分配(分配栈空间,java的线程是没有堆空间的---运行中比不可少的资源)。

          想创建java多进程,最简单的方式是多运行几个class文件,在windows的任务管理器上就会看到多个javaw.exe,这就是多个java进程,每个java进程对应一个jvm虚拟机(进程),进程间互不干扰,绝不会因为一个进程的崩溃而影响令一个进程(进程创建还有一种ProcessBuilder的方式,没用过,个人觉得这个比java的桌面程序还冷门,不想学它,然并卵)。

          想创建java线程,我就不说了。。。  资料不要太多了,也可以看我以前的博客线程学习线程学习

          至于创建java线程之后的内存模型,接上个jvm学习:

          首先有一点,一个java进程至少要有一个java线程才能创建并且运行。以普通的java文件为例,当运行main函数时,其实首先是创建一个java进程,就是先创建一个jvm虚拟机进程。这是在所有操作之前的。

          然后,开始执行正常的jvm加载和创建线程的流程,可以参考这个博客.

          多线程的情况下,比如现在jvm执行的栈帜是下面这个test方法:

public void test(){
        Test t = new Test();
        Thread t1 = MyThread();
        t1.start();
}

         首先,像执行过程3)一样,这个栈帜先查看Test类是否被加载过,如果没有加载过,进行Test类的加载和初始化。如果加载过,就在堆上创建一个Test的对象。然后执行到Thread t1 = MyThread();的时候,首先像普通的类一样,先加载并初始化这个线程类,然后在堆上创建线程类的一个实例。然后,执行t1.start()的时候,会新建一个栈,然后将t1对象的run函数入栈。也就是说,一个线程对应一个栈,栈只有出栈和入栈的操作,栈的操作单位是栈帜,栈帜对应的是方法,包含方法的局部变量和方法需要的部分成员变量和其他变量。

           深入jvm上解释,java内存模型中,线程操作的都是工作内存,对主内存中的数据,会在工作内存中有一份拷贝。线程只能在自己的工作内存中对变量进行操作,不能直接操作主内存的数据。如下图:

        综上,个人画了一个简单的图解,如果有什么错误的地方希望有大牛指出:

        首先,图解针对的是这样一段代码:

public Test{
public static void main(String[] args){
        Myth m = new Myth();                //-----------------------------1
        m.start();                                   //-----------------------------2
        test();                                       //------------------------------3
}
private static void test(){}


static class MyTh extedns Thread{
      public void run(){}
}

}

         首先,在执行java Test之后,jvm先进行一个环境的初始化,如下图:  很纯净的jvm环境,还没有执行任何个人线程,一个单纯的java进程(有java的内置线程,如加载java.lang包等,不过这个我们不关心,我们只看自己的类)


其次,在执行main函数之前,jvm会首先加载main函数所在的类(即Test类,然后将Test类的相关信息放入到方法区中)

 

 

   然后,开始执行main函数。执行main函数首先会在虚拟机栈上新建一个线程栈(姑且叫它主线程栈),(每个线程对应一个栈),然后在栈上分配main函数需要的局部变量,和将main函数需要的成员变量和其他变量拷贝到main方法的栈帜中,将main方法的栈帜压入主线程栈。

   这时候由于主线程栈中只有main方法栈帜这么一个栈帜,当然只能执行main方法了

       

 Main方法执行到1的时候,需要创建MyTh线程类的实例对象。由于是第一次访问MyTh类,所以首先要有个类加载过程,即将MyTh类信息加载进方法区,然后在堆中创建MyTh的实例对象m。

   下面开始执行2和3了。。 执行2:

         ①首先会在虚拟机栈中创建一个新的线程m的栈(暂叫m栈)

         ②然后会创建一个run方法栈帜,将m对象中的run方法需要的变量放入到run方法栈帜中,并且将run方法中需要用到的成员变量(从堆中的m实例对象拷贝)和其他对象(从方法区拷贝)拷贝到run方法栈帜中,将run方法栈帜压入线程m栈中。线程m栈此时只有一个栈帜,执行当前栈顶的栈帜。

         执行3:

         类似2中的②,创建一个test方法栈帜,初始化方法栈帜中的局部变量和拷贝其他变量,然后将test方法压入栈,执行主线程栈中的栈顶栈帜。

         上面的执行2和执行3中,其实是部分串行的,就是说2语句肯定更是先执行,但是2语句执行之后,3语句马上开始执行,不用等2执行完(个人认为是在2执行完①操作之后3就开始执行)

         在线程栈中,执行完栈顶栈帜之后,将执行完的栈帜出栈操作。出栈之前,将栈中拷贝到的非局部变量,写入主内存中(原来拷贝的地方),这个拷贝回主存的操作其实在对栈中元素(非局部变量)进行操作的时候一直在执行(拷贝回主存),并且会根据主存中数据更新工作内存的相应数据,但是jvm模型不保证对工作内存中数据的更改能马上回馈给主存(所以有了volatile关键字,见我的博客volatile的使用),所以当多个栈对主存中同一元素进行操作的时候,容易发生多线程安全问题。


 

猜你喜欢

转载自709002341.iteye.com/blog/2275353
今日推荐