1.定义
- java.util.concurrent.atomic这个包里面提供了一组原子类。其基本的特性就是在多线程环境下,当有多个线程同时执行这些类的实例包含的方法时,具有排他性,即当某个线程进入方法,执行其中的指令时,不会被其他线程打断,而别的线程就像自旋锁一样,一直等到该方法执行完成,才由JVM从等待队列中选择一个另一个线程进入,这只是一种逻辑上的理解。
- 实际上是借助硬件的相关指令(CAS)来实现的,不会阻塞线程(或者说只是在硬件级别上阻塞了)
2.分类
(1)基本类型
类AtomicBoolean、AtomicInteger、AtomicLong 、(AtomicReference )各自提供对相应类型单个变量的访问和更新。每个类也为该类型提供适当的实用工具方法。
(2)数组类型
类AtomicIntegerArray、AtomicLongArray 和AtomicReferenceArray 进一步扩展了原子操作,对这些类型的数组提供了支持。为其数组元素提供volatile 访问语义,这对于普通数组来说是不受支持的。
(3)对象的属性修改类型:
AtomicIntegerFieldUpdater, AtomicLongFieldUpdater 和 AtomicReferenceFieldUpdater
3.核心方法:
(1)compareAndSet方法
源码如下
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
(2)compareAndSwapInt方法
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
(3)compareAndSwapInt的native实现(c代码)
UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x))
UnsafeWrapper("Unsafe_CompareAndSwapInt");
oop p = JNIHandles::resolve(obj);
jint* addr = (jint *) index_oop_from_field_offset_long(p, offset);
return (jint)(Atomic::cmpxchg(x, addr, e)) == e;
(4)Atomic的cmpxchg方法
inline jint Atomic::cmpxchg (jint exchange_value, volatile jint* dest, jint compare_value) {
// alternative for InterlockedCompareExchange
int mp = os::is_MP();
__asm {
mov edx, dest
mov ecx, exchange_value
mov eax, compare_value
LOCK_IF_MP(mp)
cmpxchg dword ptr [edx], ecx
}
}
4.常用类
4.1 AtomicInteger
(1)定义:一个提供原子操作的Integer类,通过线程安全的方式操作加减,它提供原子操作来进行Integer的使用,因此十分适合高并发情况下的使用。
(2)方法
方法 | 功能 |
int addAndGet(int delta) | 以原子方式将给定值与当前值相加 |
boolean compareAndSet(int expect, int update) | 如果当前值 == 预期值,则以原子方式将该值设置为给定的更新值 |
int decrementAndGet() | 以原子方式将当前值减 1 |
double doubleValue() | 以 double 形式返回指定的数值 |
float floatValue() | 以 float 形式返回指定的数值 |
int get() | 获取当前值 |
int getAndAdd(int delta) | 以原子方式将给定值与当前值相加 |
int getAndDecrement() | 以原子方式将当前值减 1 |
int getAndIncrement() | 以原子方式将当前值加 1 |
int getAndSet(int newValue) | 以原子方式设置为给定值,并返回旧值 |
int incrementAndGet() | 以原子方式将当前值加 1 |
int intValue() | 以 int 形式返回指定的数值 |
void lazySet(int newValue) | 最后设置为给定值 |
long longValue() | 以 long 形式返回指定的数值 |
void set(int newValue) | 设置为给定值 |
String toString() | 返回当前值的字符串表示形式 |
boolean weakCompareAndSet(int expect, int update) | 如果当前值 == 预期值,则以原子方式将该设置为给定的更新值 |
(3)例:
public class AtomicTest {
public static void main(String[] args) {
AtomicDemo ad = new AtomicDemo();
for (int i = 0; i < 5; i++) {
Thread t = new Thread(ad);
t.start();
}
}
}
class AtomicDemo implements Runnable{
// private volatile int serialNumber = 0;
private AtomicInteger serialNumber = new AtomicInteger(0);
@Override
public void run() {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
}
//结果具有随机性
System.out.println(Thread.currentThread().getName()+" "+getSerialNumber());
}
public int getSerialNumber(){
//每次+1
serialNumber.getAndIncrement();
//每次+100
return serialNumber.addAndGet(100);
}
}
输出结果(不唯一):
Thread-3 202
Thread-4 102
Thread-2 303
Thread-0 505
Thread-1 404
小结:
- 原子类在具有逻辑性的情况下输出结果也具有随机性
- 因为addAndGet()是原子的,但方法和方法之间的调用不是原子的,所以需要用synchronized
4.2 LongAdder
- 在高并发的情况下,对一个 Integer 类型的整数直接进行 i++ 的时候,无法保证操作的原子性,会出现线程安全的问题。
- 一般会用 juc 下的 AtomicInteger ,它是一个提供原子操作的 Interger 类,内部也是通过 CAS 实现线程安全的。但当大量线程同时去访问时,就会因为大量线程执行 CAS 操作失败而进行空旋转,导致 CPU 资源消耗过多,而且执行效率也不高。
- Doug Lea 大神应该也不满意,于是在 JDK1.8 中对 CAS 进行了优化,提供了 LongAdder ,它是基于了 CAS 分段锁的思想实现的。
- 线程读写一个 LongAdder 类型的变量的流程图如下
- LongAdder 也是基于 Unsafe 提供的 CAS 操作 +valitale 去实现的。
- 在 LongAdder 的父类 Striped64 中维护着一个 base 变量和一个 cell 数组,当多个线程操作一个变量的时候,先会在这个 base 变量上进行 cas 操作,当它发现线程增多的时候,就会使用 cell 数组。
- 比如当 base 将要更新的时候发现线程增多(也就是调用 casBase 方法更新 base 值失败),那么它会自动使用 cell 数组,每一个线程对应于一个 cell ,在每一个线程中对该 cell 进行 cas 操作,这样就可以将单一 value 的更新压力分担到多个 value 中去,降低单个 value 的 “热度”,同时也减少了大量线程的空转,提高并发效率,分散并发压力。
- 这种分段锁需要额外维护一个内存空间 cells ,不过在高并发场景下,这点成本几乎可以忽略。
- 分段锁是一种优秀的优化思想, juc 中提供的的 ConcurrentHashMap 也是基于分段锁保证读写操作的线程安全。