《深入理解Java虚拟机》读书笔记--第十二章 Java内存模型与线程

并发处理是人类压榨计算机运算能力最有力的武器

               --------------------------谁人都有高峰和低谷,只有坚持不懈,努力不放弃,其他的交给命运

  

目录

  

本章知识概览:

1.硬件内存模型

2.编程语言的内存模型

3.说说Java内存模型 

4.一个变量如何从主内存拷贝到工作内存,如何从工作内存同步回主内存?

5.volatile的特殊规则

6.对64位数据类型的特殊规则

7.先行发生原则 happens-before

Java与线程

 8.什么是线程模型?

 9.实现线程的方式?


本章知识概览:

1.硬件内存模型

     CPU的处理速度和内存的读写速度不在一个量级,所以需要CPU之间加上一层缓存,这样就形成了CPU-寄存器-缓存-主存的访问结构,这种结构在单CPU时期运行得很好,但是多个CPU的时候,就会产生缓存一致性的问题。

  

  缓存一致性的问题:

针对这个问题科学家们设立了一个缓存同步协议,这里将同步改成了异步,也就可能出现指令的重排序,虽然出现了指令的重排序,但是指令的重排序不会影响程序执行结果的正确性。

   双核CPU的硬件结构:其中每个核都有自己的控制器,控制器包含了一组寄存器和操作控制器,运算器执行算术逻辑运算,每个核都有自己的一级缓存,二级缓存为所有的CPU共享,那么Java内存里面的工作内存就对应这硬件的L1或者L2缓存或者CPU寄存器。

 

2.编程语言的内存模型

  硬件内存模型的目标是能够让汇编代码能够运行在一个具有一致性的内存视图上,随着高级语言的流行,工程师们开始设计编程语言级别的内存模型,这是为了使用该语言进行编程的时候呢也能拥有一个一致性的内存视图,于是在硬件内存模型之上,还存在一个为编程语言设计的内存模型。

比如Java 内存模型。

3.说说Java内存模型 

    Java内存模型是Java虚拟机规范中定义的一种用来屏蔽各种硬件和操作系统访问内存的差异的模型,可以让Java程序在各种平台下都能达到一致的访问内存的效果。主要的目标是定义程序各个变量的访问规则,即在虚拟机中将变量存储到内存和从内存中取出变量这样的底层细节。

   Java内存模型规定了所有的变量都存贮在主内存,包括实例字段,静态字段,构成数组对象的元素,但是不包括局部变量与方法参数。因为他们是线程私有的。每条线程都有自己的工作内存,线程对于主存中的所有操作均不能直接进行,而是必须在自己的工作内存中进行,在工作内存中保存了主存变量的副本拷贝,线层间变量的传递均需通过主存来完成。

更加具体一点我们可以将工作线程和本地内存具象为thread stack,将主存具象为heap.在thread stack中呢有两种类型的变量,一种是原始类型的变量,存贮在线程栈上,一种是对象类型的变量,引用存在线程栈上,对象存储在线程堆中。

 java线程模型中,thread stack和heap都是对物理内存的一个抽象,线程在大部分时候,都在读写本地内存,也就是说本地内存对速度的要求很高。那么他可能就是使用寄存器和CPU缓存来实现的,而heap可能大部分就是使用硬件内存模型的主存来实现的。这里大概就能看到java内存模型和硬件内存模型模糊的映射关系。

 

4.一个变量如何从主内存拷贝到工作内存,如何从工作内存同步回主内存?

   这种机制也是线程之间的通信方式

   Java内存模型定义了八种操作来完成,虚拟机实现他们必须要是原子的。

  

  read就是把主内存的值传输到工作内存,以便随后的load动作使用,read后面必须要保证load

  store作用于工作内存,他把工作内存的变量传递到主存,以便后面的write操作使用。

 要把一个变量从主存复制到工作内存,需要顺序的执行read和load操作。

 要把变量从工作内存同步回主存,就要顺序的执行store和write操作。

5.volatile的特殊规则

 

   volatile是Java虚拟机提供的最轻量的同步机制,他能保证可见性和禁止指令重排序。

   原理?

      volatile转成汇编以后,多执行了一个将ESP寄存器的值加0的操作,这是一个空操作,这个操作相当于一个内存屏障,CPU在进行指令重排序的时候不能把内存屏障后面的指令重排序到内存屏障之前。这就是他禁止重排序的原理。

      volatile转成汇编以后,多执行了一个将ESP寄存器的值加0的操作周之前还有一个lock前缀,他的作用是使得本CPU的Cache写入了内存。这种操作相当于对Cache中的变量做了一个store,write操作,这样就可以让volatile修饰的变量对其他的CPU立即可见。Lock指令会导致处理器的缓存写回到内存,接着一个处理器的缓存写回内存会导致其他处理器的缓存无效(因为子缓存一致性协议MESI的作用下)

  除了volatile能保证可见性,还有哪些关键字能保证可见性?  final和synchronized

  synchronized能防止指令重排吗?

    可以

6.对64位数据类型的特殊规则

   虚拟机会把64位数据的读写操作当成原子操作来对待,通常的long,double.

   

7.先行发生原则 happens-before

    Java内存模型是为绕着在并发的过程中如何处理原子性,有序性,可见性这三个特征来建立的。并发过程中遇到的所有问题总结起来就这三个要素。

  先看看可见性问题:

  

一种原子性问题:

 对于可见性的问题我们可以有两层解读,第一层是由刷新主存的时机引起的可见性问题

还有一种就是由于指令重排序引起的可见性问题,Java内存模型中也存在着指令重排序。

 重排序过后可能的执行顺序,解决的办法也是加锁。

 虽然看上去很容易出现可见性的问题,但是我们在日常开发中对可见性问题没有太多的感知,那是因为我们有这个先行发生原则在约束。

  

程序顺序规则:在一个线程内,按照程序代码顺序,书写在前面的操作先行发生于书写在后面的操作

管程锁定规则:一个unlock操作先行发生于后面对同一个锁的lock操作。(lock和unlock是Java内存模型中用来保证一组操作是原子操作的指令,不直接提供这两个指令,但是提供了更高层次的字节码指令monitorrenter和monitorexit,这两个字节码指令反映到Java代码中就是synchronized关键字)

volatile变量规则:对一个volatile变量的写操作先行发生于后面对这个变量的读操作。

Java与线程

 8.什么是线程模型?

   (Java字节码运行在JVM中,JVM 运行在各个操作系统上,JVM想要进行线程创建和回收的操作时,势必需要调用操作系统的相关接口,也就是说JVM的线程与操作系统之间存在的某种映射关系,JVM抽象了这种关系,开发者 不用关注下层的实现细节,而只要关注上层的开发就行)

   线程是CPU调度的基本单位,共享进程的资源(内存地址,文件IO),又能独立调度。

 9.实现线程的方式?

   实现线程的方式主要有三种方式:使用内核线程实现,使用用户线程实现,使用用户线程加轻量级进程混合实现。

使用内核线程实现

   这种方式程序不会直接去使用内核线程,而是使用内核线程的一种高级接口----轻量级进程(LWP) ,轻量级进程和内核线程之间时一种1:1关系,这是一种一对一的线程模型。

   对于Linux系统来讲,没有专门为线程定义数据结构和调度算法,所以线程在linux系统中是一个抽象的概念,而他的具体实现就是LWP,LWP本质上也是一种进程,他没有独立的地址空间,只能共享同一个轻量级进程组下的地址地址空间。进程和轻量级进程都是使用了clone系统调用。区别仅在于向clone函数传递的参数不同。不同的参数指定是否共享地址空间等资源。

       

 优点:每个LWP都有一个独立的调度单元,即使有一个轻量级进程在系统中阻塞了也不会影响整个进程在系统中继续工作。

 缺点:基于内核线程实现,系统调用的代价非常高,降低性能,但是Java使用了AQS这样的锁来避免内核的切换。线程的数量有限。

使用用户线程实现

   用户线程的建立,同步和销毁完全在用户态中完成,不需要内核的帮助,这种进程与用户线程之间1:N的关系称为一对多的线程模型。

   

   能有有效提升第一种模式的并发量不高的问题,但是缺点显而易见啊,一个用户线程调用内核线程阻塞了的话,其他的用户线程调用内核线程就会阻塞。

使用用户线程加轻量级进程混合实现

  这种模型下用户线程与轻量级进程的数量比时不确定的为N:M关系,这就是多对多的线程模型。

这种方式有效的解决了模型一与模型二中所出现的缺点,但是头疼的是要实现这种线程模型的难度比较高。

10.Java的线程实现

   我们可以发现java.lang.Thread类的API,里面有众多的本地方法,本地方法的意思就是这个方法没有使用或者无法使用平台无关的手段来实现。而对于SunJDK来说,在Linux和Windows上使用的线程模型是一对一的模型,一条Java线程就映射到一条轻量级进程中,一条轻量级进程就映射到一条内核线程中。

 

  

  

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

时空恋旅人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值