理解AtomicXXX.lazySet方法

来源:互联网 发布:hough算法检测直线 编辑:程序博客网 时间:2024/06/07 16:21

http://blog.csdn.net/ITer_ZC/article/details/40744485

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

[java] view plain copy
  1. public final void lazySet(boolean newValue) {  
  2.         int v = newValue ? 1 : 0;  
  3.         unsafe.putOrderedInt(this, valueOffset, v);  
  4.     }  

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

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

[java] view plain copy
  1. <span><span><span style="font-family:Calibri;font-size:12px;"/*** 
  2.    * Sets the value of the integer field at the specified offset in the 
  3.    * supplied object to the given value.  This is an ordered or lazy 
  4.    * version of <code>putIntVolatile(Object,long,int)</code>, which 
  5.    * doesn't guarantee the immediate visibility of the change to other 
  6.    * threads.  It is only really useful where the integer field is 
  7.    * <code>volatile</code>, and is thus expected to change unexpectedly. 
  8.    * 设置obj对象中offset偏移地址对应的整型field的值为指定值。这是一个有序或者 
  9.    * 有延迟的<code>putIntVolatile</cdoe>方法,并且不保证值的改变被其他线程立 
  10.    * 即看到。只有在field被<code>volatile</code>修饰并且期望被意外修改的时候 
  11.    * 使用才有用。 
  12.    *  
  13.    * @param obj the object containing the field to modify. 
  14.    *    包含需要修改field的对象 
  15.    * @param offset the offset of the integer field within <code>obj</code>. 
  16.    *       <code>obj</code>中整型field的偏移量 
  17.    * @param value the new value of the field. 
  18.    *      field将被设置的新值 
  19.    * @see #putIntVolatile(Object,long,int) 
  20.    */</span></span></span>  
  21. <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说白了就是以普通变量的方式来写变量。

[java] view plain copy
  1. // 对flagA的修改对所有线程立刻可见  
  2. volatile boolean flagA;  
  3. // 对flagB的修改不能立刻被其他线程可见  
  4. boolean flagB;  

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

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


但是事实上,这里完全可以不用volatile变量来修饰这些共享状态,

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

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

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


[java] view plain copy
  1. package com.zc.lock;    
  2.     
  3. import java.util.concurrent.locks.Condition;    
  4. import java.util.concurrent.locks.ReentrantLock;    
  5.     
  6.     
  7. /**  
  8.  * 简单的可重入锁实现,使用一个计数器记录当前线程重入锁的次数,获得锁时计数器加1,释放锁时计数器减1,当计数器等于0时表示释放了锁  
  9.  * **/    
  10. public class SimpleReentrantLock implements Lock{    
  11.         
  12.     // 指向已经获得锁的线程    
  13.     private volatile Thread exclusiveOwnerThread;    
  14.         
  15.     // 记录获取了同一个锁的次数    
  16.     private volatile int holdCount;    
  17.         
  18.     private final java.util.concurrent.locks.Lock lock;    
  19.         
  20.     // 是否是自己获得锁的条件    
  21.     private final Condition isCountZero;    
  22.         
  23.     public SimpleReentrantLock(){    
  24.         lock = new ReentrantLock();    
  25.         isCountZero = lock.newCondition();    
  26.         holdCount = 0;    
  27.     }    
  28.         
  29.     @Override    
  30.     public void lock() {    
  31.         lock.lock();    
  32.         try{    
  33.             // 当前线程的引用    
  34.             Thread currentThread = Thread.currentThread();    
  35.             // 如果获得锁的线程是自己,那么计数器加1,直接返回    
  36.             if(exclusiveOwnerThread == currentThread){    
  37.                 holdCount ++;    
  38.                 return;    
  39.             }    
  40.                 
  41.             while(holdCount != 0){    
  42.                 try {    
  43.                     isCountZero.await();    
  44.                 } catch (InterruptedException e) {    
  45.                     throw new RuntimeException("Interrupted");    
  46.                 }    
  47.             }    
  48.             // 将exclusiveOwnerThread设置为自己    
  49.             exclusiveOwnerThread = currentThread;    
  50.             holdCount ++;    
  51.         }finally{    
  52.             lock.unlock();    
  53.         }    
  54.     }    

而lazySet()的用法和上面的优化是一个道理,就是在不需要让共享变量的修改立刻让其他线程可见的时候,以设置普通变量的方式来修改共享状态,可以减少不必要的内存屏障,从而提高程序执行的效率。

下面的例子来自StackOverflow上的一个提问,说的也是类似的意思,就是优化不必要的volatile操作。被墙的同学看不到,可以看截图。



0 0