zoukankan      html  css  js  c++  java
  • 聊聊高并发(十八)理解AtomicXXX.lazySet方法

    看过java.util.concurrent.atomic包里面各个AtomicXXX类实现的同学应该见过lazySet方法。比方AtomicBoolean类的lazySet方法

    public final void lazySet(boolean newValue) {
            int v = newValue ? 1 : 0;
            unsafe.putOrderedInt(this, valueOffset, v);
        }

    它的底层实现调用了Unsafe的putOrderedInt方法。来看看putOrderedXXX方法的JavaDoc

    它的意思是putOrderedXXX方法是putXXXVolatile方法的延迟实现,不保证值的改变被其它线程马上看到

    <span><span><span style="font-family:Calibri;font-size:12px;"> /***
       * Sets the value of the integer field at the specified offset in the
       * supplied object to the given value.  This is an ordered or lazy
       * version of <code>putIntVolatile(Object,long,int)</code>, which
       * doesn't guarantee the immediate visibility of the change to other
       * threads.  It is only really useful where the integer field is
       * <code>volatile</code>, and is thus expected to change unexpectedly.
       * 设置obj对象中offset偏移地址相应的整型field的值为指定值。这是一个有序或者
       * 有延迟的<code>putIntVolatile</cdoe>方法。而且不保证值的改变被其它线程立
       * 即看到。仅仅有在field被<code>volatile</code>修饰而且期望被意外改动的时候
       * 使用才实用。

       *    * @param obj the object containing the field to modify.    *    包括须要改动field的对象    * @param offset the offset of the integer field within <code>obj</code>.    *       <code>obj</code>中整型field的偏移量    * @param value the new value of the field.    *      field将被设置的新值    * @see #putIntVolatile(Object,long,int)    */</span></span></span> <span><span><span style="font-family:Calibri;font-size:12px;"> public native void putOrderedInt(Object obj, long offset, int value);</span></span></span>


    理解volatile底层实现的同学知道,volatile的实现终于是加了内存屏障,

    1. 保证写volatile变量会强制把CPU写缓存区的数据刷新到内存

    2. 读volatile变量时,使缓存失效。强制从内存中读取最新的值

    3. 由于内存屏障的存在,volatile变量还能阻止重排序


    所以volatile变量的改动能够立马让全部的线程可见,保证了可见性。

    而不加volatile变量的字段。JMM不保证普通变量的改动立马被全部的线程可见。所以lazySet说白了就是以普通变量的方式来写变量。

    // 对flagA的改动对全部线程立马可见
    volatile boolean flagA;
    // 对flagB的改动不能立马被其它线程可见
    boolean flagB;

    那么为什么须要lazySet方法呢?事实上它是一种低级别的优化手段,对上层调用者来说。事实上非常少用到。以下说说用lazySet的一个场景。

    在这篇 聊聊高并发(十六)实现一个简单的可重入锁 中我们实现了一个可重入锁。里面共享变量用了volatile变量,来保证对共享变量的改动对其它线程可见。


    可是事实上,这里全然能够不用volatile变量来修饰这些共享状态。

    1. 由于訪问共享状态之前先要获得锁, Lock.lock()方法能够获得锁,而获得锁的操作和volatile变量的读操作一样,会强制使CPU缓存失效,强制从内存读取变量。

    2. Lock.unlock()方法释放锁时。和写volatile变量一样,会强制刷新CPU写缓冲区。把缓存数据写到主内存

    底层也是通过加内存屏障实现的。


    package com.zc.lock;  
      
    import java.util.concurrent.locks.Condition;  
    import java.util.concurrent.locks.ReentrantLock;  
      
      
    /** 
     * 简单的可重入锁实现,使用一个计数器记录当前线程重入锁的次数,获得锁时计数器加1,释放锁时计数器减1。当计数器等于0时表示释放了锁 
     * **/  
    public class SimpleReentrantLock implements Lock{  
          
        // 指向已经获得锁的线程  
        private volatile Thread exclusiveOwnerThread;  
          
        // 记录获取了同一个锁的次数  
        private volatile int holdCount;  
          
        private final java.util.concurrent.locks.Lock lock;  
          
        // 是否是自己获得锁的条件  
        private final Condition isCountZero;  
          
        public SimpleReentrantLock(){  
            lock = new ReentrantLock();  
            isCountZero = lock.newCondition();  
            holdCount = 0;  
        }  
          
        @Override  
        public void lock() {  
            lock.lock();  
            try{  
                // 当前线程的引用  
                Thread currentThread = Thread.currentThread();  
                // 假设获得锁的线程是自己。那么计数器加1,直接返回  
                if(exclusiveOwnerThread == currentThread){  
                    holdCount ++;  
                    return;  
                }  
                  
                while(holdCount != 0){  
                    try {  
                        isCountZero.await();  
                    } catch (InterruptedException e) {  
                        throw new RuntimeException("Interrupted");  
                    }  
                }  
                // 将exclusiveOwnerThread设置为自己  
                exclusiveOwnerThread = currentThread;  
                holdCount ++;  
            }finally{  
                lock.unlock();  
            }  
        }  

    而lazySet()的使用方法和上面的优化是一个道理,就是在不须要让共享变量的改动立马让其它线程可见的时候,以设置普通变量的方式来改动共享状态,能够降低不必要的内存屏障,从而提高程序运行的效率。

    以下的样例来自StackOverflow上的一个提问,说的也是相似的意思,就是优化不必要的volatile操作。

    被墙的同学看不到。能够看截图。


  • 相关阅读:
    Android修改app的图标
    关于Python3中的WARNING: Retrying (Retry(total=4, connect=None, read=None, redirect=None, status=None)) after connection broken by 'ReadTimeoutError("HTTPSConnectionPool(host='pypi.org', port=443):
    Android 7.1 虚拟按键(NavigationBar)源码分析 控件加载、属性控制隐藏
    Android5.1 Browser 闪退分析 /data/tombstones
    RootCommand
    RK3288 Linux4.4.143 适配EETI
    Android 7.1 Camera2 拍照镜像分析
    Docker pull slow resolution
    Dockerfile使用OracleJDK创建自定义tomcat8镜像
    centos使用docker安装ActiveMQ
  • 原文地址:https://www.cnblogs.com/llguanli/p/8404500.html
Copyright © 2011-2022 走看看