zoukankan      html  css  js  c++  java
  • 并发中关键字的语义

    一、volatile的内存语义

    1. 简单的举例

    可以把对volatile变量的单个读/写,看成是使用同一个锁对这些单个读/写操作做了同步;但是复合操作是不生效的;

    package com.youyou.ch1.demo;
    
    public class Vola {
        volatile int a = 1; //使用volatile 声明int类型的变量
    
        public int getA()
        {
            return a; //对单个volatile 变量进行读
        }
    
        public void setA(int a)
        {
            this.a = a; //对单个volatile 变量进行写
        }
        public void inc()
        {
            a++; //对复合(多个)volatile 变量进行 读/写
        }
    }
    View Code

    这样的代用 volatile 关键字的代码和下面的是一样的;

    package com.youyou.ch1.demo;
    
    public class VolaLikeSyn {
        int a = 0; //普通的变量
    
        public synchronized int getA()
        {
            return a; //对单个普通 变量进行读
        }
    
        public synchronized void setA(int a)
        {
            this.a = a; //对单个普通 变量进行写
        }
        public void inc() //普通方法的调用
        {
            int temp = getA(); //调用同步方法
            temp = temp + 1; //普通的写操作
            setA(temp); //调用同步的方法
        }
    }
    View Code

    2. volatile变量自身具有下列特性:

    • 可见性。对一个volatile变量的读,总是能看到(任意线程)对这个volatile变量最后的写入。
    • 原子性:对任意单个volatile变量的读/写具有原子性,但类似于volatile++这种复合操作不具有原子性。

    2.1 volatile写的内存语义如下:当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量值刷新到主内存。

    2.2 volatile读的内存语义如下:当读一个volatile变量时,JMM会把该线程对应的本地内存置为无效。线程接下来将从主内存中读取共享变量。

    3. JMM对volatile的内存屏障插入策略

    • 在每个volatile写操作的前面插入一个StoreStore屏障。在每个volatile写操作的后面插入一个StoreLoad屏障。
    • 在每个volatile读操作的后面插入一个LoadLoad屏障。在每个volatile读操作的后面插入一个LoadStore屏障。

     4. volatile的实现原理

    有volatile变量修饰的共享变量进行写操作的时候会使用CPU提供的Lock前缀指令。(这个Lock指令,使我们在反编译的时候也看不见是,是汇编层面的关键字)

    将当前处理器缓存行的数据写回到系统内存 ,这个写回内存的操作会使在其他CPU里缓存了该内存地址的数据无效。

    二、锁的内存语义

    • 当线程释放锁时,JMM会把该线程对应的本地内存中的共享变量刷新到主内存中。
    • 当线程获取锁时,JMM会把该线程对应的本地内存置为无效。从而使得被监视器保护的临界区代码必须从主内存中读取共享变量。

    synchronized的实现原理:

    1. 使用monitorenter和monitorexit指令实现的

    • monitorenter指令是在编译后插入到同步代码块的开始位置,而monitorexit是插入到方法结束处和异常处
    • 每个monitorenter必须有对应的monitorexit与之配对
    • 任何对象都有一个monitor与之关联,当且一个monitor被持有后,它将处于锁定状态
    • sychronized“方法”通常不是用monitorenter和monitorexit指令实现的。往往是由“方法调用指令”检查常数池里的ACC_SYCHRONIZED标志

    2. 了解各种锁

    锁一共有4种状态,级别从低到高依次是:无锁状态、偏向锁状态、轻量级锁状态和重量级锁状态

    2.1 偏向锁

    大多数情况下,锁不仅不存在多线程竞争,而且总是由同一线程多次获得,为了让线程获得锁的代价更低而引入了偏向锁。无竞争时不需要进行CAS操作来加锁和解锁。

    2.2 轻量级锁

    无竞争时通过CAS操作来加锁和解锁。

    2.3 重量级锁

    三、final的内存语义

    1. 编译器和处理器要遵守两个重排序规则。

    • 在构造函数内对一个final域的写入,与随后把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序。
    • 初次读一个包含final域的对象的引用,与随后初次读这个final域,这两个操作之间不能重排序。

     2. final域为引用类型

    • 增加了如下规则:在构造函数内对一个final引用的对象的成员域的写入,与随后在构造函数外把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序。

    3. final语义在处理器中的实现

    • 会要求编译器在final域的写之后,构造函数return之前插入一个StoreStore障屏。
    • 读final域的重排序规则要求编译器在读final域的操作前面插入一个LoadLoad屏障
  • 相关阅读:
    【python】第一个爬虫:用requests库爬取网页内容
    【python】UDP协议编程
    【python】体育竞技分析:预测球队比赛成绩
    【python】手绘图制作
    【python】成绩表雷达图
    【python】numpy库和matplotlib库学习笔记
    【python】PIL库之图片处理
    【python】PIL库学习总结
    【python】利用jieba中文分词进行词频统计及生成词云
    汉诺塔问题
  • 原文地址:https://www.cnblogs.com/lys-lyy/p/11152452.html
Copyright © 2011-2022 走看看