什么是原子类
- 不可分割
- 一个操作是不可中断的
作用和锁类似,是为了保证并发情况下的线程安全,不过原子类相比于锁,有一定的优势。
- 粒度更细:原子变量可以把竞争范围缩小到变量级别,这是我们可以获得最细粒度的情况了,通常锁的粒度都要大于原子变量的粒度
- 效率更高,通常,使用原子类的效率比使用锁的效率更高。
6类原子类总览
- 基本类型原子类
- AtomicInteger
- AtomicLong
- AtomicBoolean
- 数组类型原子类
- AtomicIntegerArray
- AtomicLongArray
- AtomicReferenceArray
- 引用类型原子类
- AtomicReference
- AtomicStampedReference
- AtomicMarkableReference
- 升级类型原子类
- AtomicIntegerfieldupdater
- AtomicLongFieldUpdater
- AtomicReferenceFieldUpdater
- Adder累加器
- LongAdder
- DoubleAdder
- Accumulator累加器
- LongAccumulator
- DoubleAccumulator
Atomic基本类型原子类
常用方法
- get()获取当前的值
- getAndSet(int newValue)获取当前的值,并设置新值
- getAndIncrement()获取当前的值,并自增
- getAndDecrement()获取当前的值,并自减
- getAndAdd(int delta)获取当前的值并加上预期的值
- compareAndSet(int expect,int update)如果当前的数值等于预期值,则以原子方式将该值设置为输入值
AtomicInteger
public class AtomicIntegerDemo1 implements Runnable{
private static final AtomicInteger atomicInteger = new AtomicInteger();
public void incrementAtomic(){
atomicInteger.getAndIncrement();
}
private static volatile int basicCount = 0;
public void incrementBasic(){
basicCount++;
}
public static void main(String[] args) {
AtomicIntegerDemo1 r = new AtomicIntegerDemo1();
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(atomicInteger);
System.out.println(basicCount);
}
@Override
public void run() {
for(int i = 0;i < 10000;i++){
incrementBasic();
incrementAtomic();
}
}
}
AtomicIntegerfieldupdater
public class AtomicIntegerFieldUpdaterDemo implements Runnable{
static Candidate tom;
static Candidate peter;
public static AtomicIntegerFieldUpdater<Candidate> scoreUpdater
= AtomicIntegerFieldUpdater.newUpdater(Candidate.class,"score");
@Override
public void run() {
for(int i = 0; i < 10000; i++){
peter.score++;
scoreUpdater.getAndIncrement(tom);
}
}
public static void main(String[] args) {
tom = new Candidate();
peter = new Candidate();
AtomicIntegerFieldUpdaterDemo r = new AtomicIntegerFieldUpdaterDemo();
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(tom.score);
System.out.println(peter.score);
}
}
累加器Adder
高并发下LongAdder与AtomicLong效率高,不过本质是空间换时间,竞争激烈的时候,LongAdder把不同线程对应到不同的Cell上进行修改,降低了冲突的概率,是多段锁的理念,提高了并发性。
public class LongAdderDemo {
public static void main(String[] args) {
LongAdder counter = new LongAdder();
ExecutorService service = Executors.newFixedThreadPool(20);
for(int i = 0; i < 10000; i++){
service.submit(new Task(counter));
}
service.shutdown();
while(!service.isTerminated()){
}
System.out.println(counter.sum());
}
private static class Task implements Runnable{
private LongAdder counter;
public Task(LongAdder counter){
this.counter = counter;
}
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
counter.increment();
}
}
}
}
AtomicLong的性能远远不如LongAdder。AtomicLong每一次假发都要flush和refresh,导致很耗费时间。LongAdder不需要保持线程之间的同步,不会通过内存做中转。再内部,这个LongAdder的实现原理和刚才的AtomicLong是由不同的,刚才的AtomicLong的实现原理和刚才的AtomicLong是有不同的,刚才的AtomicLong的实现原理是,每一次假发都需要做同步,所以再高并发的时候会导致冲突比较多,也就降低了效率。而LongAdder,每个线程会有自己的一个计数器,仅用来再自己线程内技术,这样一来就不会和其他线程的计数器造成干扰。
LongAdder引入了分段累加的概念,内部有一个base变量和一个Cell[]数组共同参与计数:
- base变量:竞争不激烈,直接累加到该变量上
- Cell[]数组:竞争激烈,各个线程分散累加到自己的槽Cell [i]中
sum源码
还没看
CAS原理
- 什么是CAS
- 案例演示
- 应用场景
什么是CAS(CompairAndSwap)
用于并发,
我认为V的值应该是A,如果是的话那我就把它改成B,如果不是A(说明被别人修改过了),那么我就不修改了,避免多人同时修改导致出错。
CAS有三个操作数:内存值V、预期值A、要修改的值B,当且仅当预期值A和内存值V相同时,才将内存值修改为B,否则什么都不做。最后返回现在的V值。
CAS的等价代码
public class SimulatedCAS {
private volatile int value;
public synchronized int compareAndSwap(int expectedValue,int newValue){
int oldValue = value;
if(oldValue == expectedValue){
value = newValue;
}
return oldValue;
}
}
应用场景
- 乐观锁
- 并发容器
- 原子类
分析Java中如何利用CAS实现原子操作
- AtomicInteger加载Unsafe工具,用来直接操作内存数据
- 用Unsafe来实现底层操作
- 用volatile修饰value字段,保证可见性
- getAndAddInt方法分析
原子类中的getAndAdd方法的实现
public final int getAndAdd(int delta){
return unsafe.getAndAddInt(this,valueOffset,delta);
}
这里使用Unsafe这个类,这里简要介绍一下Unsafe
Unsafe是CAS的核心类,Java无法直接访问底层操作系统,而是通过本地native方法来访问。不过尽管如此,JVM还是开了一个后门,JDK中有一个类Unsafe,它提供了硬件级别的的原子操作。
AtomicInteger加载Unsafe工具,用来直接操作内存数据
public class AtomicInteger extends Number implements java.io.Serializable{
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 final int get(){return value;}
}
再AtomicInteger数据定义的部分,我们还获取了unsafe实例,并且定义了valueOffset。在看到static块,由类加载过程我们可以直到static块加载发生于类加载的时候,是最先初始化的,这时候我们调用unsafe的objectFieldOffset从Atomic类文件中获取value的偏移量,那么valueOffset其实就是记录value的偏移量的。valueOffset表示的是变量值再内存中的偏移地址,因为Unsafe就是根据内存偏移地址获取数据的原值的,这样我们就能通过unsafe来实现CAS了,value是用volatile修饰的,保证了多线程之间看到value值是同一份。下面时unsafe类的getAndAddInt方法实现。
public final int getAndAddInt(Object var1,long var2,int var4){
int var5;
do{
var5 = this.getIntVolatile(var1,var2);
} while(!this.compareAndSwapInt(var1,var2,var5,var5+var4));
return var5;
}
var5获取的就是通过调用unsafe的getIntVolatile(var1,var2),这是个native方法,其实就是获取var1中,var2偏移量处的值。var1就是AtomicInteger,var2就是我们前面提到的valueOffset,这样我们就可以从内存里获取现在valueOffset处的值了。
CAS的缺点
- ABA问题 添加版本号来解决。
- 并发竞争太大时,自旋时间过长。