美文网首页
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

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