Java高并发(四)–Java内存模型浅析以及JVM内存模型

释放双眼,带上耳机,听听看~!

目录

文章目录

  • 目录
    * 1、什么是Java内存模型
    * 2、原子性
    * 3、可见性
    * 4、有序性
    * 5、JVM内存模型

1、什么是Java内存模型

​ 描述的是一组规则或规范,通过这组规范定义了程序中各个变量的访问方式。

​ 围绕原子性、有序性和可见性展开。

2、原子性

​ 原子性是指操作是不可分的,要么全部一起执行,要么不执行。

​ 实现原子性方法大致有两种:

  • 锁机制
  • 无锁CAS机制

3、可见性

​ 可见性是值一个线程对共享变量的修改,对于另一个线程来说是否是可以看到的。

  • 定义的所有变量都储存在 主内存中
  • 每个线程都有自己 独立的工作内存,里面保存该线程使用到的变量的副本(主内存中该变量的一份拷贝)
  • 线程对共享变量所有的操作都必须在自己的工作内存中进行,不能直接从主内存中读写(不能越级)
  • 不同线程之间也无法直接访问其他线程的工作内存中的变量,线程间变量值的传递需要通过主内存来进行。(同级不能相互访问)

线程需要修改一个共享变量X,需要先把X从主内存复制一份到线程的工作内存,在自己的工作内存中修改完毕之后,再从工作内存中回写到主内存。如果线程对变量的操作没有刷写回主内存的话,仅仅改变了自己的工作内存的变量的副本,那么对于其他线程来说是不可见的。而如果另一个变量没有读取主内存中的新的值,而是使用旧的值的话,同样的也可以列为不可见。

共享变量可见性的实现原理:

  1. .线程A在自己的工作内存中修改变量之后,需要将变量的值刷新到主内存中
  2. 线程B要把主内存中变量的值更新到工作内存中

实现方法:

  1. volatile
  2. synchronized

4、有序性

有序性指的是程序按照代码的先后顺序执行。

为了性能优化,编译器和处理器会进行指令冲排序,有时候会改变程序语句的先后顺序,比如程序。

比如单例模式的双重校验锁的实现:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1public class Singleton {
2    private static Singleton instance;
3    public static Singleton getInstance() {
4        if (instance == null){
5            Synchronized(Singleton.class){
6                if (instance == null){
7                    instance = new Singleton();
8                }
9            }
10        }
11        return instance;
12    }
13}
14
15

我们来看instance实例过程:

未被编译器优化的操作:

  1. 指令1:分配一款内存M
  2. 指令2:在内存M上初始化Singleton对象
  3. 指令3:将M的地址赋值给instance变量

编译器优化后的操作指令:

  1. 指令1:分配一块内存S
  2. 指令2:将M的地址赋值给instance变量
  3. 指令3:在内存M上初始化Singleton对象

此时如果有两个线程,刚好执行的代码被优化过:

最终线程B获取的instance是没有初始化的,此时去使用instance可能会产生一些意想不到的错误。

所以双重校验锁可以通过添加
volatile关键字来禁止指令重排序。

单例模式双重校验锁:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
1Public class Singleton{
2   Private volatile static Singleton Instance = null;
3   Private Singleton(){
4   }
5   Public static Singleton getInstance(){
6       If(Instance == null){
7           Synchronized(Singleton.class){
8               If(Instance == null){
9                   Instance = new Singleton();
10              }
11          }
12      }
13      Return Instance;
14  }
15}
16
17

静态内部类实现:


1
2
3
4
5
6
7
8
9
10
11
12
1               Public class Singleton{
2                   Private Singleton(){
3                   }
4                   Private static class SIngletonHolder{
5                       Private static final Singleton Instance = new Singleton();
6                   }
7                   Public static Singleton getInstance(){
8                       Return SingletonHolder.Instance;
9                   }
10}
11
12

5、JVM内存模型

线程公有的包括堆、方法区,线程私有的包括虚拟机栈、本地方法栈、程序计数器。

  1. 程序计数器:程序执行的字节码行号指示器
  2. 虚拟机栈:每个方法执行都会创建一个栈帧,栈帧有一个局部变量表, 存储着局部变量、对象引用地址、返回地址等信息
  3. 本地方法栈:作用同上,为 native 方法服务
  4. 方法区:jdk1.8 之前的元空间,存储类加载信息
  5. 堆:主要存储对象和数组,分 2/3 新生代(0.8Eden,0.1 存活区*2)、1/3 老年代 l 常量池:移到了堆中,存放静态变量、常量、符号引用

给TA打赏
共{{data.count}}人
人已打赏
安全经验

Google Adsense老手经验

2021-10-11 16:36:11

安全经验

安全咨询服务

2022-1-12 14:11:49

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索