JVM高级特性与最佳实战(五)————Java内存模型

引言

本来想着给大家介绍下JVM虚拟机类的加载过程来着,实在是抽象,不好组织语言,甚至好多都不想看了。所以我们从Java的内存模型讲起来,然后分析下高效并发的问题和解决方案。

由来

Java虚拟机规范中试图定义一种Java内存模型(JMM)来屏蔽各种硬件和操作系统的内存访问差异性,这样就能够在各种平台上都能达到Java程序一致的内存访问效果。以前的C++都是直接使用物理硬件和操作系统的内存模型,所以会对各个操作系统之间的程序运行结果有差异。

Java内存模型定义的主要目标是定义程序中各个变量的访问规则。即在虚拟机中将变量存储到内存和从内存中取出变量的底层细节。

主内存和工作内存

  • Java内存模型规定了所有变量都在主内存中
  • 每条线程还具有自己的工作内存。
  • 线程的工作内存中保存了被该线程使用到的变量的主内存副本拷贝。
  • 线程对变量的所有操作都必须在工作内存中进行,不能直接读写主内存的变量。

volatile型变量的特殊规则

  • volatile能够保证变量的可见性,但是不能保证线程安全。
    可见性是指当一条线程修改了变量的值,新值对于其他线程来说是可以立即得知的。
    普通变量的值在线程之间传递均需要通过主内存来完成,例如,线程A修改一个普通变量的值,然后向主内存进行回写。另外一个线程B在线程A写完之后再从主内存进行读取操作,新变量值才对线程B可见。
    但是,volatile变量在各个线程的工作内存内可能会有不同,因为每次使用之前都会刷新这个变量,所以才让执行引擎认为没有不一致。所以,我们知道这个关键字修饰的变量也不能保证线程安全。
  • volatile能防止指令重排。这个我在https://blog.csdn.net/qq_15612527/article/details/96293004叙述的比较明白了,大家不懂的去看一下。

long double型变量的特殊规则

Java内存模型要求内存间的操作都具有原子性,但是对于64位的数据类型,允许虚拟机实现选择可以不保证64位数据类型的某些操作的原子性,这个就是long和double的非原子性协定。但是虚拟机厂商一般都把long double实现为原子性的操作,所以我们声明的时候不用加volatile。

原子性,可见性,有序性

  • 原子性:在代码级别的原子性是由lock和unlock 以及synchronized关键字保证的
  • 可见性:代码级别中通过volatile synchronized和final可以实现。需要提及的是被final修饰的字段在构造器中一旦初始化完成,并且构造器没有把this的关键字传递出去(this引用逃逸是一种很危险的事情,其他线程可能通过这个引用访问到初始化了一半的对象),那么其他线程就能看到这个值。
  • 有序性:java提供了volitail和synchronized两个关键字来保证线程之间操作的有序性。

先行发生的原则

这个原则非常重要,他是判断数据是否存在竞争,线程是否安全的主要依据,依靠这个原则,我们可以通过几条规则一揽子解决并发环境下两个操作之间是否可能存在冲突的所有问题。

  • 程序次序规则:一个线程内,按照代码执行顺序,书写在前面的操作先行发生后边的操作。
  • 管程锁定规则:一个unlock操作先行发生于对同一个锁的lock操作。
  • volatile变量规则:先读后写。
  • 线程启动规则:Thread对象的start方法先行发生。
  • 线程终止规则:所有操作先行发生于此线程的终止检测。可以用join isAlive检测线程终止。

总结

都是一些理论,大家仔细体会。如果有疑问自行百度吧~这种概念型的不好举例子。


作者:select you from me
来源:CSDN
转载请联系作者获得授权并注明出处。

你可能感兴趣的:(JVM高级特性与最佳实战)