美文网首页
java多线程-3-volatile

java多线程-3-volatile

作者: 宠辱不惊的咸鱼 | 来源:发表于2019-09-30 09:02 被阅读0次

缓存一致性问题

  • 起因:高速缓存
  • 方案
    • 总线LOCK#锁,释放锁前该块内存无法被别的cpu或线程访问
    • 缓存一致性协议(Intel的MESI协议),cpu写共享变量时,会通知其它cpu将缓存失效

并发性质

  • 原子性:事务、原语
  • 可见性:值在高速缓存中被改变,未来得及写入内存,别的线程去读就错了
  • 有序性(JVM指令重排)
    语句1和2被重排,下面的多线程可能就挂了
// 线程1 - 负责加载context,然后发通知
context = loadContext(); // 语句1
inited = true;           // 语句2

//线程2 - 监听inited通知信号,业务处理
while(!inited) {
    sleep();
}
doSomethingWithConfig(context);

JMM Java内存模型

  • 线程先将堆中对象属性拷到工作内存,修改,然后刷回堆
  • 原子性:synchronized/Lock
  • 可见性
    • volatile:读值时强制从主内存读;值改变后会马上刷入主内存
    • synchronized/Lock:出同步前刷入主内存
  • 有序性
    • volatile
      • 读写volatile变量时,前面操作已完成,后面操作未开始
      • 语句重排时无法突破volatile边界
      • 上面例子中,给inited属性加volatile即可
    • happens-before(先行发生)原则
      • 程序次序:单线程内,先写的代码先执行(这个是从结果角度出发,和指令重排不矛盾)
      • 锁定规则:同一个锁,必先unlock,再lock
      • volatile变量规则:一个线程先写,一个线程后读,那么必先写完
      • 传递规则:A先于B,B先于C,则A先于C
      • 线程启动:Thread的start方法是该线程的第一个动作
      • 线程中断:interrupt方法的调用先于被中断线程检测到中断信号
      • 线程终结:所有操作先于终止检测
      • 对象终结:对象初始化的结束,早于finalize方法的开始

实现机制

  • volatile会在汇编代码中增加一个lock前缀指令(相当于内存屏障)
    • 确保指令重排时不会把其后面的指令排到其之前,也不会把前面的指令排到内存屏障后面;执行到内存屏障指令时,前面操作均已完成
    • 强制将对缓存的修改立即写入主内存
    • 写操作会导致其他CPU中对应缓存无效

举例场景

// 只有volatile
public class Test {
    private volatile int inc = 0;
    public void increase() {
        inc++; // 线程1和2同时读入inc,线程2快速+1写回主内存;由于线程1已读过值,直接开加,所以错了
    }
    public static void main(String[] args) {
        final Test test = new Test();
        for(int i = 0; i < 10; i++) {
            new Thread(() -> {
                for(int j = 0; j < 1000; j++) {
                    test.increase();
                }
            }).start();
        }
        while(Thread.activeCount() > 1) {
            Thread.yield();
        }
        System.out.println(test.inc);
    }
}

// synchronized,阻塞了从主内存取值的动作
private int inc = 0;
public synchronized void increase() {
    inc++;
}

// Lock
private int inc = 0;
Lock lock = new ReentrantLock();
public void increase() {
    try {
        lock.lock();
        inc++;
    } finally {
        lock.unlock();
    }
}

// AtomicInteger
private AtomicInteger inc = new AtomicInteger();
public void increase() {
    inc.getAndIncrement();
}

public final int getAndIncrement() {
    return unsafe.getAndAddInt(this, valueOffset, 1);
}

public final int getAndAddInt(Object var1, long var2, int var4) {
    int var5;
    do {
        var5 = this.getIntVolatile(var1, var2);
    } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

    return var5;
}
  • AtomicInteger -> Unsafe.CAS -> 处理器CMPXCHG指令(原子指令)

相关文章

  • java多线程-3-volatile

    缓存一致性问题 起因:高速缓存 方案总线LOCK#锁,释放锁前该块内存无法被别的cpu或线程访问缓存一致性协议(I...

  • 带你搞懂Java多线程(五)

    带你搞懂Java多线程(一)带你搞懂Java多线程(二)带你搞懂Java多线程(三)带你搞懂Java多线程(四) ...

  • 带你搞懂Java多线程(六)

    带你搞懂Java多线程(一)带你搞懂Java多线程(二)带你搞懂Java多线程(三)带你搞懂Java多线程(四)带...

  • Java多线程目录

    Java多线程目录 Java多线程1 线程基础Java多线程2 多个线程之间共享数据Java多线程3 原子性操作类...

  • java多线程--Callable

    **移步[java多线程系列文章]Java多线程(二十二)---LockSupport工具Java 停止线程 一、...

  • android 多线程 — 线程的面试题和答案

    这里都是我从各个地方找来的资料,鸣谢: Java多线程干货系列—(一)Java多线程基础 JAVA多线程和并发基础...

  • 5月份第一周学习安排

    学习内容: java多线程及线程同步的方法(使用) java多线程各种同步方法的原理和优缺点 java多线程设计模...

  • 带你搞懂Java多线程(四)

    带你搞懂Java多线程(一)带你搞懂Java多线程(二)带你搞懂Java多线程(三) 什么是线程间的协作 线程之间...

  • Java基础(六)

    多线程 Java多线程并发 1.1 JAVA 并发知识库 1.2 JAVA 线程实现/创建方式 1.2.1 继承 ...

  • (五) volatile关键字

    Java多线程目录 1 背景 理解Java多线程的内存抽象逻辑请阅读java多线程内存模型,当代操作系统,处理器为...

网友评论

      本文标题:java多线程-3-volatile

      本文链接:https://www.haomeiwen.com/subject/uprcpctx.html