【学习笔记】—JVM(五)内存模型与线程

一、Java内存模型

在这里插入图片描述
  创建这样一个模型主要是为了定义程序中各个变量的访问规则。这也是Java跨平台性的一个重要组成部分。因为例如C/C++等语言都是直接使用物理硬件和操作系统的内存模型,会出现在这个平台能够正常访问数据,换一个之后就报错访问不到或者访问到错误数据。
  图中的内存这些和前面的堆,栈,方法区,划分层次不同基本没有什么关系

特征
  • 原子性
    在一个操作中,CPU不可以在中途停止然后再调度,即不被中断操作,要么执行完成,要么不执行
  • 可见性
    当多线程访问同一个变量,一个线程修改了该变量的值,其他线程可立即看到修改的值
  • 有序性
    当多线程访问同一个变量,一个线程修改了该变量的值,其他线程可立即看到修改的值。

Java天然有序性:在本程序重观察,所有的操作都是有序的;在一个线程中观察另一个线程,所有的操作都是无序的。前半句指“线程内表现为串行语义”后半句指“指令重排序”现象和“工作内存与主内存同步延迟”现象。

保证原子性的8钟操作
操作作用范围描述
lock(锁定)主内存把一个变量标记为一条线程独占状态
unlock(解锁)主内存将一个处于锁定状态的变量释放出来,释放后的变量才能够被其他线程锁定
read(读取)工作内存把变量值从主内存传送到线程的工作内存中,以便随后的load动作使用
load(载入)工作内存它把read操作的值放入工作内存中的变量副本中
use(使用)工作内存把工作内存中的值传递给执行引擎,每当虚拟机遇到一个需要使用这个变量的指令时候,将会执行这个动作
assig(赋值)工作内存把从执行引擎获取的值赋值给工作内存中的变量,每当虚拟机遇到一个给变量赋值的指令时候,执行该操作
store(存储)工作内存把工作内存中的一个变量传送给主内存中,以备随后的write操作使用
write(写)主内存把store传送值放到主内存中的变量中
  • read和load,store和write要按顺序执行,且不能单一出现
  • use、store操作之前,必须先执行load、assign,即新变量只能在主内存中新生
  • lock一个变量,将会清空工作内存中此变量的值,在需要他的时候重新执行load或者assig对其初始化值

二、volatile变量

  最轻量的同步机制
  两种特性:
    - 被修饰的变量对所有线程有可见性。
      在对volatile做修改时相当于对变量做了内存交互操作的store和write操作,由此对其他CPU立即可见
    - 禁止指令重排序
      通过内存屏障来实现,即重排序时不能把后面的指令重排序到内存屏障之前的位置。

指令重排序:为了尽可能减少内存操作速度远慢于CPU运行速度所带来的CPU空置的影响,虚拟机会按照自己的一些规则(这规则后面再叙述)将程序编写顺序打乱——即写在后面的代码在时间顺序上可能会先执行,而写在前面的代码会后执行——以尽可能充分地利用CPU

  volatile修饰的变量只能保证可见性而不能保证原子性,任然需要通过加锁才能保证原子性,因此volatile变量只有在满足以下条件才使用

  • 运算结果并不依赖变量的当前值,或者能够确保只有单一的线程修改变量的值。
  • 变量不需要与其他的状态变量共同参与不变约束。

  volatile变量读写性能消耗与普通变量相差无多,而且大多数情况下volatile变量的总开销要比锁低。

三、其他关键字的可见性

  synchronized和final也能实现可见性,同步块的可见性是对一个变量执行unlock之前,必须把此变量同步回主内存中(store、write)。而被final修饰的字段在构造器中一旦初始化完成,并且构造器没有把this的引用传递出去,那其他线程中就能看见final字段的值

四、先行发生原则

  总不可能所有的有序性都用关键子来规定。先行发生是判断数据是都存在竞争,线程是否安全的主要依据。比如操作A产生的影响(改变值,发送消息,调用等)能被操作B观察到,则A必然是要先于B发生的。

//A线程发生
i=1;

//B线程发生
j=i;

//C线程发生
i=2;

没有C时,J必然是1,有了C,C和B没有先行发生,那么执行的顺序时A->B->C还是A->C->B就不能确定了。

天然先行发生关系
  • 程序次序规则
  • 管程锁定规则
  • volatile变量规则
  • 线程启动规则
  • 线程终止规则
  • 线程中断规则
  • 对象终结规则
  • 传递性

“时间上的先发生”不一定代表“先行发生”

五、Java与线程

线程的实现
  • 内核线程实现(1对1)
    内核线程(KTL),操作系统内核支持的线程。程序一般不会直接使用内核线程,而是去使用内核现成的高级接口——轻量级进程(LWP),也就是我们通常意义上的线程
    在这里插入图片描述
    这样系统调用的代价相对较高,且每个轻量级进程都要内核线程支持,要消耗一定的内核资源,因此一个系统支持轻量级进程的数量是有限的。
  • 用户线程实现(1对多)
    只要不是内核线程的都是用户线程(UT)
    在这里插入图片描述
    因为没有内核线程的支援,所有的操作都要用户程序自己处理
  • 用户线程加轻量级进程混合实现(多对多)
    混合使用
    在这里插入图片描述
Java线程的调度方式为抢占式线程调度
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值