转自https://www.cnblogs.com/rever/p/8215743.htmlhtml
在进行并发编程的时候咱们须要确保程序在被多个线程并发访问时能够获得正确的结果,也就是实现线程安全。线程安全的定义以下:java
当多个线程访问某个类时,无论运行时环境采用何种调度方式或者这些线程将如何交替执行,而且在主调代码中不须要任何额外的同步或协同,这个类都能表现出正确的行为,那么这个类就是线程安全的。编程
举个线程不安全的例子。假如咱们想实现一个功能来统计网页访问量,你可能想到用count++
来统计访问量,可是这个自增操做不是线程安全的。count++
能够分红三个操做:缓存
假设count的初始值为10,当进行并发操做的时候,可能出现线程A和线程B都进行到了1操做,以后又同时进行2操做。A先进行到3操做+1,如今值为11;注意刚才AB获取到的当前值都是10,因此B执行3操做后,count的值依然是11。这个结果显然不符合咱们的要求。安全
因此咱们须要用本篇的主角—— AtomicInteger 来保证线程安全。并发
AtomicInteger 的源码以下:app
package java.util.concurrent.atomic; import sun.misc.Unsafe; public class AtomicInteger extends Number implements java.io.Serializable { private static final long serialVersionUID = 6214790243416807050L; // setup to use Unsafe.compareAndSwapInt for updates private static final Unsafe unsafe = Unsafe.getUnsafe(); private static final long valueOffset; static { try { valueOffset = unsafe.objectFieldOffset (AtomicInteger.class.getDeclaredField("value")); } catch (Exception ex) { throw new Error(ex); } } private volatile int value; public AtomicInteger(int initialValue) { value = initialValue; } public AtomicInteger() { } public final int get() { return value; } public final void set(int newValue) { value = newValue; } public final void lazySet(int newValue) { unsafe.putOrderedInt(this, valueOffset, newValue); } public final int getAndSet(int newValue) { for (;;) { int current = get(); if (compareAndSet(current, newValue)) return current; } } public final boolean compareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); } public final boolean weakCompareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); } public final int getAndIncrement() { for (;;) { int current = get(); int next = current + 1; if (compareAndSet(current, next)) return current; } } public final int getAndDecrement() { for (;;) { int current = get(); int next = current - 1; if (compareAndSet(current, next)) return current; } } public final int getAndAdd(int delta) { for (;;) { int current = get(); int next = current + delta; if (compareAndSet(current, next)) return current; } } public final int incrementAndGet() { for (;;) { int current = get(); int next = current + 1; if (compareAndSet(current, next)) return next; } } public final int decrementAndGet() { for (;;) { int current = get(); int next = current - 1; if (compareAndSet(current, next)) return next; } } public final int addAndGet(int delta) { for (;;) { int current = get(); int next = current + delta; if (compareAndSet(current, next)) return next; } } public String toString() { return Integer.toString(get()); } public int intValue() { return get(); } public long longValue() { return (long)get(); } public float floatValue() { return (float)get(); } public double doubleValue() { return (double)get(); } }
咱们先看原子整型类中定义的属性工具
// setup to use Unsafe.compareAndSwapInt for updates private static final Unsafe unsafe = Unsafe.getUnsafe(); private static final long valueOffset; static { try { valueOffset = unsafe.objectFieldOffset (AtomicInteger.class.getDeclaredField("value")); } catch (Exception ex) { throw new Error(ex); } }
Unsafe是JDK内部的工具类,主要实现了平台相关的操做。下面内容引自性能
sun.misc.Unsafe是JDK内部用的工具类。它经过暴露一些Java意义上说“不安全”的功能给Java层代码,来让JDK可以更多的使用Java代码来实现一些本来是平台相关的、须要使用native语言(例如C或C++)才能够实现的功能。该类不该该在JDK核心类库以外使用。this
Unsafe的具体实现跟本篇的目标关联不大,你只要知道这段代码是为了获取value在堆内存中的偏移量就够了。偏移量在AtomicInteger中很重要,原子操做都靠它来实现。
AtomicInteger 自己是个整型,因此最重要的属性就是value,咱们看看它是如何声明value的
private volatile int value;
咱们看到value使用了volatile
修饰符,那么什么是volatile
呢?
volatile至关于synchronized
的弱实现,也就是说volatile
实现了相似synchronized
的语义,却又没有锁机制。它确保对volatile
字段的更新以可预见的方式告知其余的线程。
volatile
包含如下语义:
- Java 存储模型不会对valatile指令的操做进行重排序:这个保证对volatile变量的操做时按照指令的出现顺序执行的。
- volatile变量不会被缓存在寄存器中(只有拥有线程可见)或者其余对CPU不可见的地方,每次老是从主存中读取volatile变量的结果。也就是说对于volatile变量的修改,其它线程老是可见的,而且不是使用本身线程栈内部的变量。也就是在happens-before法则中,对一个valatile变量的写操做后,其后的任何读操做理解可见此写操做的结果。
简而言之volatile 的做用是当一个线程修改了共享变量时,另外一个线程能够读取到这个修改后的值。在分析AtomicInteger 源码时,咱们了解到这里就足够了。
AtomicInteger中有不少方法,例如incrementAndGet()
至关于i++
和getAndAdd()
至关于i+=n
。从源码中咱们能够看出这几种方法的实现很类似,因此咱们主要分析incrementAndGet()
方法的源码。
源码以下:
public final int incrementAndGet() { for (;;) { int current = get(); int next = current + 1; if (compareAndSet(current, next)) return next; } } public final boolean compareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); }
incrementAndGet()
方法实现了自增的操做。核心实现是先获取当前值和目标值(也就是value+1),若是compareAndSet(current, next)
返回成功则该方法返回目标值。那么compareAndSet是作什么的呢?理解这个方法咱们须要引入CAS操做。
在大学操做系统课程中咱们学过独占锁和乐观锁的概念。独占锁就是线程获取锁后其余的线程都须要挂起,直到持有独占锁的线程释放锁;乐观锁是先假定没有冲突直接进行操做,若是由于有冲突而失败就重试,直到操做成功。其中乐观锁用到的机制就是CAS,Compare and Swap。
AtomicInteger 中的CAS操做就是compareAndSet()
,其做用是每次从内存中根据内存偏移量(valueOffset
)取出数据,将取出的值跟expect 比较,若是数据一致就把内存中的值改成update。
这样使用CAS就保证了原子操做。其他几个方法的原理跟这个相同,在此再也不过多的解释。
没看AtomicInteger 源码以前,我认为其内部是用synchronized
来实现的原子操做。查阅资料后发现synchronized
会影响性能,由于Java中的synchronized
锁是独占锁,虽然能够实现原子操做,可是这种实现方式的并发性能不好。
总结一下,AtomicInteger 中主要实现了整型的原子操做,防止并发状况下出现异常结果,其内部主要依靠JDK 中的unsafe 类操做内存中的数据来实现的。volatile 修饰符保证了value在内存中其余线程能够看到其值得改变。CAS操做保证了AtomicInteger 能够安全的修改value 的值。