J.U.C中的原子变量

  在开发过程中,很多时候都需要用到原子的递增递减操作;而我们知道,常用的i ++ 和 i -- 等操作都不是原子的,它包含了三步操作(读-改-写):首先,读取变量i的值,其次将i执行 +1 或者 -1 操作,最后,将计算后的结果赋值给i;通常情况下,只有加锁才能保证 i ++ 和 i -- 等操作的原子性。值得关注的是,在JDK1.5及之后的所有版本,java.concurrent.util.atomic包下给我们提供了原子变量,原子变量支持原子的递增和递减操作。这里,针对原子变量的底层实现原理及jdk的部分源码,做一些简单的记录。

  1:AtomicBoolean,值得注意的是,AtomicBoolean使用了int类型的属性value来表示true和false,在设置、获取及修改的时候,都是针对value的操作。

  其中,AtomicLong、AtomicInteger源码与AtomicBoolean相差不大,所以这里不再赘述。

/**
 * @Comment AtomicBoolean部分源码
 */
public class AtomicBoolean implements Serializable {
    /**
     * sun.misc.Unsafe类
     */
    private static final Unsafe unsafe = Unsafe.getUnsafe() ;
    /**
     * 属性value的偏移量
     */
    private static final long valueOffset ;
    /**
     * 使用int类型value来表示true和false
     * 1 - true     0 - false     * volatile 修饰,可以保证value的可见性;     *  即:如果一个线程对value进行了修改,会立马同步到主内存;同时其它线程的本地内存失效,直接读取主内存获取value的值。
     */
    private volatile int value ;

    static {
        try {
            //获取AtomicBoolean中value属性的内存地址偏移量
            valueOffset = unsafe.objectFieldOffset(AtomicBoolean.class.getDeclaredField("value")) ;
        } catch (Exception e) {
            throw new Error(e) ;
        }
    }

    /**
     * 初始化  true-1 false-0
     * @param initiavlValue
     */
    public AtomicBoolean(boolean initiavlValue){
        this.value = initiavlValue ? 1 : 0 ;
    }
    public AtomicBoolean(){}

    /**
     * 获取当前值
     * @return
     */
    public final boolean get(){
        return value != 0 ;
    }
    /**
     * 原子的更新
     * @param expect    期望值
     * @param update    修改值
     * @return
     */
    public final boolean compareAndSet(boolean expect , boolean update){
        int e = expect ? 1 : 0 ;
        int u = update ? 1 : 0 ;
        //原子的修改value的值,如果value == e ; 修改为u ;否则不修改 ; 返回是否修改成功
        return unsafe.compareAndSwapInt(this , valueOffset , e , u) ;
    }
    /**
     * 原子的更新
     * @param expect    期望值
     * @param update    修改值
     * @return
     */
    public final boolean weakCompareAndSet(boolean expect , boolean update){
        int e = expect ? 1 : 0 ;
        int u = update ? 1 : 0 ;
        //原子的修改value的值,如果value == e ; 修改为u ;否则不修改 ; 返回是否修改成功
        return unsafe.compareAndSwapInt(this , valueOffset , e , u) ;
    }
    /**
     * 设置value的值
     * @param newValue
     */
    public final void set(boolean newValue){
        value = newValue ? 1 : 0 ;
    }
    /**
     * 最终设置值
     * @param newValue
     */
    public final void lazySet(boolean newValue){
        int v = newValue ? 1 : 0 ;
        unsafe.putOrderedInt(this , valueOffset , v) ;
    }
    /**
     * 获取老值,设置新值
     * @param newValue
     * @return
     */
    public final boolean getAndSet(boolean newValue){
        boolean prev ;
        do {
            prev = get() ;
        }while (!compareAndSet(prev , newValue)) ;
        return prev ;
    }
    public String toString(){
        return Boolean.toString(get()) ;
    }
}

  2:AtomicIntegerArray

  1 /**
  2  * @Comment AtomicIntegerArray部分源码
  3  */
  4 public class AtomicIntegerArray implements Serializable {
  5
  6     private static final Unsafe unsafe = Unsafes.get() ;
  7     /**
  8      * 对象分配时,获取int类型数组的第一个元素的内存地址
  9      * 即:相对于数组对象起始地址的偏移地址
 10      */
 11     private static final int base = unsafe.arrayBaseOffset(int[].class) ;
 12     /**
 13      * shift就是用来定位数组中的内存位置,用来移位用的;每向左移动移位,在不越界的情况下,想当于乘以2。
 14      * 由于int类型的长度为4,所以,数组第0个角标开始位是0,第1(i)个角标开始位是4,,第二个(i)位置是8,
 15      * 也就是偏移位置等于  i * 4,也就是  i << 2;总结出一个乘法转换成移位操作的案例:
 16      * a * (一个2的幂(n)的数)  =  a << n; 给出一个指定2的幂的数,怎么算成n,,参照shift的计算方法
 17      */
 18     private static final int shift ;
 19     /**
 20      * int[]数组,在构造函数中初始化
 21      */
 22     private final int[] array ;
 23
 24     /**
 25      *  1: unsafe.arrayIndexScale(int[].class) ;
 26      *      标识int数组类型中元素占用字节的个数.
 27      *  2: if( ( scale & (scale - 1) ) != 0 ) ;
 28      *      验证元素占用的字节是否为2的n次幂
 29      *  3: Integer.numberOfLeadingZeros(scale) ;
 30      *      标识int类型的scale转换为2进制存储时,前边补0的个数
 31      *      31 - Integer.numberOfLeadingZeros(scale) (这里跟内存操作相关,暂时没搞懂为什么用31减...)
 32      */
 33     static {
 34         int scale = unsafe.arrayIndexScale(int[].class) ;
 35         if( ( scale & (scale - 1) ) != 0 ){
 36             throw new Error("data type scale not a power of two") ;
 37         }
 38         shift = 31 - Integer.numberOfLeadingZeros(scale) ;
 39     }
 40     /**
 41      * 判断角标是否越界
 42      * @param i
 43      * @return
 44      */
 45     private long checkedByteOffset(int i){
 46         if(i < 0 || i >= array.length)
 47             throw new IndexOutOfBoundsException("index" + i) ;
 48         return byteOffset(i) ;
 49     }
 50     /**
 51      * 返回数组中第i个角标存储的元素的地址偏移量
 52      * @param i
 53      * @return
 54      */
 55     private static long byteOffset(int i){
 56         return (long) (i << shift) + base ;
 57     }
 58     /**
 59      * 根据长度,构造int[]
 60      * @param length
 61      */
 62     public AtomicIntegerArray(int length){
 63         this.array = new int[length] ;
 64     }
 65     /**
 66      * 根据传入的数组,构造int[]
 67      * @param array
 68      */
 69     public AtomicIntegerArray(int [] array){
 70         this.array = array.clone() ;
 71     }
 72     /**
 73      * 获取int[]的长度
 74      * @return
 75      */
 76     public final int length(){
 77         return array.length ;
 78     }
 79     /**
 80      * 获取数组的第i个元素
 81      * @param i
 82      * @return
 83      */
 84     public final int get(int i){
 85         return getRaw(i) ;
 86     }
 87     /**
 88      * 获取array第offset角标的值
 89      * @param offset
 90      * @return
 91      */
 92     private int getRaw(long offset){
 93         return unsafe.getIntVolatile(array , offset) ;
 94     }
 95     /**
 96      * 将第i个角标的值设置为新值
 97      * @param i
 98      * @param newValue
 99      */
100     public final void set(int i , int newValue){
101         unsafe.putIntVolatile(array , checkedByteOffset(i) , newValue) ;
102     }
103     /**
104      * 延迟设置值
105      * @param i
106      * @param newValue
107      */
108     public final void lazySet(int i , int newValue){
109         unsafe.putOrderedInt(this , checkedByteOffset(i) , newValue) ;
110     }
111     /**
112      * 获取并设置值
113      * @return
114      */
115     public final int getAndSet(int i , int newValue){
116         return unsafe.getAndSetInt(this , checkedByteOffset(i) , newValue) ;
117     }
118     /**
119      * 先比较,后设置
120      * @param i
121      * @param expect
122      * @param update
123      * @return
124      */
125     public final boolean compareAndSet(int i , int expect , int update){
126         return compareAndSetRaw(checkedByteOffset(i) , expect , update) ;
127     }
128     private boolean compareAndSetRaw(long offset , int expect , int update){
129         return unsafe.compareAndSwapInt(array , offset , expect , update) ;
130     }
131     public final boolean weakCompareAndSet(int i , int expect , int update){
132         return compareAndSet(i , expect , update) ;
133     }
134     /**
135      * 线程安全的递增
136      * @param i
137      * @return
138      */
139     public final int getAndIncrement(int i){
140         return getAndAdd(i, 1) ;
141     }
142     /**
143      * 线程安全的递减
144      * @param i
145      * @return
146      */
147     public final int getAndDecrement(int i){
148         return getAndAdd(i , -1) ;
149     }
150     /**
151      * 为角标为i的元素 加 delta
152      * @param i
153      * @param delta
154      * @return
155      */
156     public final int getAndAdd(int i , int delta){
157         return unsafe.getAndAddInt(array , checkedByteOffset(i) , delta) ;
158     }
159     /**
160      * 线程安全的递增,并获取递增后的值
161      * @return
162      */
163     public final int incrementAndGet(int i){
164         return getAndAdd(i , 1) + 1 ;
165     }
166     /**
167      * 线程安全的获取递减后的值
168      * @param i
169      * @return
170      */
171     public final int decrementAndGet(int i){
172         return getAndAdd(i , -1) - 1 ;
173     }
174     public final int addAndGet(int i , int delta){
175         return getAndAdd(i , delta) + delta ;
176     }
177
178     @Override
179     public String toString() {
180         int iMax = array.length - 1 ;
181         if(iMax == -1){
182             return "[]" ;
183         }
184         StringBuilder b = new StringBuilder() ;
185         b.append(‘[‘) ;
186         //两个;;之间没有判断,计算机可以少一步计算,提高性能
187         for(int i = 0 ; ; i ++){
188             b.append(getRaw(byteOffset(i))) ;
189             if(i == iMax){
190                 return b.append(‘]‘).toString() ;
191             }
192             b.append(‘,‘).append(‘ ‘) ;
193         }
194     }
195 }

  3:AtomicReference

 1 /**
 2  * @Comment AtomicReference部分源码 5  */
 6 public class AtomicReference<V> implements Serializable {
 7
 8     private static final Unsafe unsafe = Unsafe.getUnsafe() ;
 9     /**
10      * 对象地址
11      */
12     private static final long valueOffset ;
13     /**
14      * 待操作对象
15      */
16     private volatile V value ;
17     static {
18         try {
19             valueOffset = unsafe.objectFieldOffset(AtomicReference.class.getDeclaredField("value")) ;
20         }catch (Exception e){
21             throw new Error(e) ;
22         }
23     }
24
25     /**
26      * 构造器
27      * @param initValue
28      */
29     public AtomicReference(V initValue){
30         this.value = initValue ;
31     }
32     public AtomicReference(){}
33
34     /**
35      * 获取当前value
36      * @return
37      */
38     public final V get(){
39         return this.value ;
40     }
41     /**
42      * 设置当前value
43      * @param newValue
44      */
45     public final void set(V newValue){
46         this.value = newValue ;
47     }
48     /**
49      * 延迟设置
50      * @param newValue
51      */
52     public final void lazySet(V newValue){
53         unsafe.putOrderedObject(this , valueOffset , newValue);
54     }
55     /**
56      * 如果对象为期望对象,原子的替换为给定的对象
57      * @param expect
58      * @param update
59      * @return
60      */
61     public final boolean compareAndSet(V expect , V update){
62         return unsafe.compareAndSwapObject(this , valueOffset , expect , update) ;
63     }
64     /**
65      * 如果对象为期望对象,原子的替换为给定的对象
66      * @param expect
67      * @param update
68      * @return
69      */
70     public final boolean weakCompareAndSet(V expect , V update){
71         return unsafe.compareAndSwapObject(this , valueOffset , expect , update) ;
72     }
73     /**
74      * 原子的设置
75      * @param newValue
76      * @return
77      */
78     public final V getAndSet(V newValue){
79         return (V) unsafe.getAndSetObject(this , valueOffset , newValue) ;
80     }
81     @Override
82     public String toString() {
83         return String.valueOf(get()) ;
84     }
85 }
时间: 2024-11-11 04:37:17

J.U.C中的原子变量的相关文章

Java并发编程实战 第15章 原子变量和非阻塞同步机制

非阻塞的同步机制 简单的说,那就是又要实现同步,又不使用锁. 与基于锁的方案相比,非阻塞算法的实现要麻烦的多,但是它的可伸缩性和活跃性上拥有巨大的优势. 实现非阻塞算法的常见方法就是使用volatile语义和原子变量. 硬件对并发的支持 原子变量的产生主要是处理器的支持,最重要的是大多数处理器架构都支持的CAS(比较并交换)指令. 模拟实现AtomicInteger的++操作 首先我们模拟处理器的CAS语法,之所以说模拟,是因为CAS在处理器中是原子操作直接支持的.不需要加锁. public s

多线程并发编程之原子变量与非阻塞同步机制

1.非阻塞算法 非阻塞算法属于并发算法,它们可以安全地派生它们的线程,不通过锁定派生,而是通过低级的原子性的硬件原生形式 —— 例如比较和交换.非阻塞算法的设计与实现极为困难,但是它们能够提供更好的吞吐率,对生存问题(例如死锁和优先级反转)也能提供更好的防御.使用底层的原子化机器指令取代锁,比如比较并交换(CAS,compare-and-swap). 2.悲观技术 独占锁是一种悲观的技术.它假设最坏的情况发生(如果不加锁,其它线程会破坏对象状态),即使没有发生最坏的情况,仍然用锁保护对象状态.

个人笔记--内存可见性和原子变量

jdk1.6以后提供了java并发包. volatile与内存可见性: 例子: 结果: 结论: main()线程读取到的td.isFlag并不是true. 这就涉及到了内存可见性问题. 具体原因: 重排序:代码书写的顺序与实际执行的顺序不同. 1.  编译器重排序 2.  指令重排序 3.  内存系统重排序 As-if-serial: 无论如何重排序,程序执行的记过应该与代码顺序执行的结果一致.Java编译器和处理器在运行时都会保证在单线程下遵循这个语言. 补充:jstack可以生成线程快照(j

Java并发——原子变量和原子操作与阻塞算法

十五年前,多处理器系统是高度专用系统,要花费数十万美元(大多数具有两个到四个处理器).现在,多处理器系统很便宜,而且数量很多,几乎每个主要微处理器都内置了多处理支持,其中许多系统支持数十个或数百个处理器. 要使用多处理器系统的功能,通常需要使用多线程构造应用程序.但是正如任何编写并发应用程序的人可以告诉你的那样,要获得好的硬件利用率,只是简单地在多个线程中分割工作是不够的,还必须确保线程确实大部分时间都在工作,而不是在等待更多的工作,或等待锁定共享数据结构. 问题:线程之间的协调 如果线程之间 

Java原子变量类需要注意的问题

在学习多线程时,遇到了原子变量类,它是基于 CAS 和 volatile 实现的,能够保障对共享变量进行 read-modify-write 更新操作的原子性和可见性.于是我就写了一段代码试试,自认为非常正确. public class Test{ private static AtomicInteger ID = new AtomicInteger(0); public static int nextID(){ //返回的ID范围为 1~100 if(ID.get() == 100) { //

聊聊高并发(二十)解析java.util.concurrent各个组件(二) 12个原子变量相关类

这篇说说java.util.concurrent.atomic包里的类,总共12个.网上有非常多文章解析这几个类.这里挑些重点说说. 这12个类能够分为三组: 1. 普通类型的原子变量 2. 数组类型的原子变量 3. 域更新器 普通类型的原子变量的6个, 1. 当中AtomicBoolean, AtomicInteger, AtomicLong, AtomicReference分别相应boolean, int,  long, object完毕主要的原子操作 2. AtomicMarkableRe

java中的Volatile 变量

Java 语言中的 volatile 变量可以被看作是一种 “程度较轻的 synchronized”:与 synchronized 块相比,volatile 变量所需的编码较少,并且运行时开销也较少,但是它所能实现的功能也仅是 synchronized 的一部分.本文介绍了几种有效使用 volatile 变量的模式,并强调了几种不适合使用 volatile 变量的情形. 锁提供了两种主要特性:互斥(mutual exclusion) 和可见性(visibility).互斥即一次 只允许一个线程持

《Java并发编程实战》第十五章 原子变量与非阻塞同步机制 读书笔记

一.锁的劣势 锁定后如果未释放,再次请求锁时会造成阻塞,多线程调度通常遇到阻塞会进行上下文切换,造成更多的开销. 在挂起与恢复线程等过程中存在着很大的开销,并且通常存在着较长时间的中断. 锁可能导致优先级反转,即使较高优先级的线程可以抢先执行,但仍然需要等待锁被释放,从而导致它的优先级会降至低优先级线程的级别. 二.硬件对并发的支持 处理器填写了一些特殊指令,例如:比较并交换.关联加载/条件存储. 1 比较并交换 CAS的含义是:"我认为V的值应该为A,如果是,那么将V的值更新为B,否则不需要修

java并发编程(8)原子变量和非阻塞的同步机制

原子变量和非阻塞的同步机制 一.锁的劣势 1.在多线程下:锁的挂起和恢复等过程存在着很大的开销(及时现代的jvm会判断何时使用挂起,何时自旋等待) 2.volatile:轻量级别的同步机制,但是不能用于构建原子复合操作 因此:需要有一种方式,在管理线程之间的竞争时有一种粒度更细的方式,类似与volatile的机制,同时还要支持原子更新操作 二.CAS 独占锁是一种悲观的技术--它假设最坏的情况,所以每个线程是独占的 而CAS比较并交换:compareAndSwap/Set(A,B):我们认为内存