栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

线程之间数据交换——线程通信

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

线程之间数据交换——线程通信

线程通信

文章目录
  • 线程通信
  • 前言
  • 一、复习一下JVM的内存划分
    • 1、PC保存区
    • 2、栈
          • (1)虚拟机栈
          • (2)本地方法栈
    • 3、堆
    • 4、方法区
    • 5、运行时常量池
  • 二、线程中的内存划分
    • 1、PC保存区
    • 2、栈
    • 3、堆
    • 4、方法区
    • 5、运行时常量池
    • 6、总结
  • 三、代码中是如何体现的
  • 总结


前言

大部分场景下,几个线程之间是需要协调配合工作,一起完成一个总目标。所以,线程之间需要数据交换。由于线程都属于同一个进程,而进程又是OS进行资源分配的基本单位,所以互相配合的线程们共同享有OS分配的资源。


一、复习一下JVM的内存划分 1、PC保存区

程序计数器(Program Counter Register),也有称作为PC寄存器,保存下一条指令的所在存储单元的地址。JVM中的PC保存区就是用来保存PC的值。

2、栈 (1)虚拟机栈

虚拟机栈也称Java栈,也就是我们常常所说的栈。Java栈中存放的是一个个的栈帧,每个栈帧对应一个被调用的方法,在栈帧中包括局部变量表(Local Variables)、操作数栈(Operand Stack)、指向当前方法所属的类的运行时常量池(运行时常量池的概念在方法区部分会谈到)的引用(Reference to runtime constant pool)、方法返回地址(Return Address)和一些额外的附加信息。

(2)本地方法栈

本地方法栈与虚拟机栈(Java栈)的作用和原理非常相似。区别只不过是虚拟机栈是为执行Java方法服务的,而本地方法栈则是为执行本地方法(Native Method)服务的。在JVM规范中,并没有对本地方发展的具体实现方法以及数据结构作强制规定,虚拟机可以自由实现它。在HotSopt虚拟机中直接就把本地方法栈和Java栈合二为一。

3、堆

Java中的堆是用来存储对象本身的以及数组(需要注意的是,数组引用是存放在Java栈中的)。这部分空间也是Java垃圾收集器管理的主要区域。

4、方法区

跟Java堆一样,方法区是各个线程共享的内存区域,此区域是用来存储类的信息(类的名称、字段信息、方法信息)、静态变量、常量以及编译器编译后的代码。

5、运行时常量池

在方法区中有一个非常重要的部分就是运行时常量池,它是每一个类或接口的常量池的运行时表示形式,在类和接口被加载到JVM后,对应的运行时常量池就被创建出来。当然并非Class文件常量池中的内容才能进入运行时常量池,在运行期间也可将新的常量放入运行时常量池中。

二、线程中的内存划分

我们都知道,进程是OS进行资源分配的基本单位,然后一个进程又可能包含多个进程,所以从线程的角度看,内存又是如何划分的呢?

1、PC保存区

每个线程都是一条独立的执行流,因此,为了能够使得每个线程都在线程切换后能够恢复在切换之前的程序执行位置,每个线程都需要有自己独立的程序计数器,并且不能互相被干扰,否则就会影响到程序的正常执行次序。因此,可以这么说,程序计数器是每个线程所私有的。

2、栈

栈中存放的是一个个的栈帧,每个栈帧对应一个被调用的方法,由于每个线程正在执行的方法可能不同,要处理的临时数据也不同,因此每个线程都会有一个自己的Java栈,互不干扰。

3、堆

Java中的堆是用来存储对象本身的以及数组这部分空间也是Java垃圾收集器管理的主要区域。在JVM中只有一个堆,堆是被所有线程共享的。

4、方法区

方法区是用来存储类的信息(类的名称、字段信息、方法信息)、静态变量、常量以及编译器编译后的代码。是被线程共享的区域。

5、运行时常量池

是每一个类或接口的常量池的运行时表示形式,运行时常量池也是共享资源。

6、总结
  • PC保存区、栈(虚拟机栈和本地方法栈)是线程私有的,线程之间互不干扰;
  • 堆、方法区、运行时常量池是线程共享的,整个进程(JVM)只有一份。

我去百度了一张图,帮助我们更好的理解线程的内存分配情况(有点子糊,大家见谅哈)

三、代码中是如何体现的
  • 局部变量:保存在栈帧(栈)中,所以是线程私有的。
  • 类对象(Class对象):静态属性,保存在方法区中,所以是线程共享的。
  • 普通对象:保存在堆中,所以是线程共享的(前提是线程持有该对象的引用)。

写个简单的demo看一下吧

public class dateShare {
	//static int r=0;如果把 r 变量放在这里,r 就是静态变量,存放在方法区中,就变成线程共享的。
    public static void main(String[] args) throws InterruptedException {
    	//这个 r 是主线程的 r(暂且把主方法的线程称为主线程,其他线程即为子线程)
        int r=0;
        add t=new add();
        t.start();
        //等待 t 线程运行结束
        t.join();
        System.out.println(Thread.currentThread().getName()+":"+r);//0
    }
    static class add extends Thread{
        @Override
        public void run() {
        	//这个 r 是子线程的 r 
            int r=0;
            for (int i = 0; i < 1000; i++) {
                r++;
            }
            System.out.println(Thread.currentThread().getName()+":"+r);//1000
        }
    }
}

运行结果如下图所示:

可以看到,主方法中打印 r=0,而add类的run()方法中打印r=1000。
因为 r 是局部变量,保存在栈帧中,是线程私有的。所以主线程的 r 变量依然是 0 ,而子线程运行结束后,r 自增到 1000。这俩 r 只是名字相同而已。


总结

线程之间数据交换的内容就到这里啦,拜拜‍♀️‍♀️‍♀️‍♀️

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/961595.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号