- 原子类的作用和锁类似,是为了保证并发情况下的线程安全。不过原子类相比锁,有一定优势
- 粒度更细:他锁的范围更小
- 效率更高:相比于锁,效率更高,除了高度竞争的情况
- Atomic*基本类型原子类:AtomicInteger、AtomicLong、AtomicBoolean
- Atomic*Array数组类型原子类:AtomicIntegerArray、AtomicLongArray、AtomicBooleanArray
- Atomic*Reference引用类型原子类:AtomicReference、AtomicStampedRefence、AtomicMarkableRenfence
- Atomic*FieldUpdater升级类型原子类:AtomicIntegerfiedupdater、AtomicLongFieldUpdater、AtomicRefenceFieldUpdater
- Adder累加器:LongAdder、DoubleAdder
- Accumulator累加器:LongAccumulator、DoubleAccumulator
基于CAS操作实现原子性
3.1、AtomicInteger常用方法
- get()获取当前的值
- getAndSet(int newValue)获取当前的值,并设置新的值
- getAndIncrement()获取当前的值,并自增
- getAndDecrement()获取当前的值,并自减
- getAndAdd(int delta)获取当前的值,并加上预期的值
- compareAndSet(int expect, int update)如果当前的值等于预期的值,则以原子方式将值设置为输入值
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 void run() { for (int i = 0; i < 10000; i++) { incrementAtomic(); incrementBasic(); } } public static void main(String[] args) throws InterruptedException { AtomicIntegerDemo1 r = new AtomicIntegerDemo1(); Thread thread1 = new Thread(r); Thread thread2 = new Thread(r); thread1.start(); thread2.start(); thread1.join(); thread2.join(); System.out.println("原子类的结果" + atomicInteger.get()); System.out.println("普通变量的结果" + basicCount); } }
可以保证一个数组中所有的原子操作
public class AtomicArrayDemo { public static void main(String[] args) throws InterruptedException { AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(1000); Incrementer incrementer = new Incrementer(atomicIntegerArray); Decrementer decrementer = new Decrementer(atomicIntegerArray); Thread[] threadsIncrement = new Thread[100]; Thread[] threadsDecrement = new Thread[100]; for (int i = 0; i < 100; i++) { threadsDecrement[i] = new Thread(decrementer); threadsIncrement[i] = new Thread(incrementer); threadsDecrement[i].start(); threadsIncrement[i].start(); } for (int i = 0; i < 100; i++) { threadsDecrement[i].join(); threadsIncrement[i].join(); } for (int i = 0; i < atomicIntegerArray.length(); i++) { if (atomicIntegerArray.get(i) != 0){ System.out.println("发现了非0值,位置"+i); } } System.out.println("运行结束"); } } class Decrementer implements Runnable{ private AtomicIntegerArray array; public Decrementer(AtomicIntegerArray array){ this.array = array; } public void run() { for (int i = 0; i < array.length(); i++) { array.getAndDecrement(i); } } } class Incrementer implements Runnable{ private AtomicIntegerArray array; public Incrementer(AtomicIntegerArray array){ this.array = array; } public void run() { for (int i = 0; i < array.length(); i++) { array.getAndIncrement(i); } } }
和AtomicInteger本质上没有区别,这个是针对于对象
public class SpinLock { private AtomicReference
sign = new AtomicReference<>(); public void loock(){ Thread currentThread = Thread.currentThread(); while (!sign.compareAndSet(null, currentThread)){ System.out.println("自旋获取失败,再次尝试"); } } public void unlock(){ Thread currentThread = Thread.currentThread(); sign.compareAndSet(currentThread, null); } public static void main(String[] args) { SpinLock spinLock = new SpinLock(); Runnable runnable = new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName() + "尝试获取自旋锁"); spinLock.loock(); System.out.println(Thread.currentThread().getName() + "获取到了自旋锁"); try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } finally { spinLock.unlock(); System.out.println(Thread.currentThread().getName() + "释放了自旋锁"); } } }; new Thread(runnable).start(); new Thread(runnable).start(); } } 主要是使用了compareAndSet()方法
将普通变量升级为具有原子功能
public class AtomicIntegerFieldUpdaterDemo implements Runnable { static Candidate tom; static Candidate peter; public static AtomicIntegerFieldUpdater
scoreUpdater = AtomicIntegerFieldUpdater.newUpdater(Candidate.class, "score"); public void run() { for (int i = 0; i < 10000; i++) { peter.score++; // tom.score++; scoreUpdater.getAndIncrement(tom); } } public static class Candidate{ volatile int score; } public static void main(String[] args) throws InterruptedException { tom = new Candidate(); peter = new Candidate(); AtomicIntegerFieldUpdaterDemo r = new AtomicIntegerFieldUpdaterDemo(); Thread thread1 = new Thread(r); Thread thread2 = new Thread(r); thread1.start(); thread2.start(); thread1.join(); thread2.join(); System.out.println("普通的变量:"+peter.score); System.out.println("升级后的的变量:"+tom.score); } } 使用的是反射的机制,且不支持被static修饰的变量
- 是Java8引入的,想对是比较新的一个类
- 高并发下LongAdder比AtomicLong效率高,不过本质是空间换时间
- 竞争激励的时候,LongAdder把不同线程对应到不同的Cell上进行修改,降低了冲突的概率,是多段锁的理念,提高了并发性
AtomicLong的耗时
public class AtomicLongDemo { public static void main(String[] args) { AtomicLong counter = new AtomicLong(0); ExecutorService executorService = Executors.newFixedThreadPool(20); long start = System.currentTimeMillis(); for (int i = 0; i < 10000; i++) { executorService.submit(new Task(counter)); } executorService.shutdown(); while (!executorService.isTerminated()){ } long end = System.currentTimeMillis(); System.out.println("耗费时间="+(end-start)); } private static class Task implements Runnable{ private AtomicLong counter; public Task(AtomicLong counter){ this.counter = counter; } public void run() { for (int i = 0; i < 10000; i++) { counter.incrementAndGet(); } } } }
使用LongAdder提升性能
public class LongAdderDemo { public static void main(String[] args) { LongAdder counter = new LongAdder(); ExecutorService executorService = Executors.newFixedThreadPool(20); long start = System.currentTimeMillis(); for (int i = 0; i < 10000; i++) { executorService.submit(new LongAdderDemo.Task(counter)); } executorService.shutdown(); while (!executorService.isTerminated()){ } long end = System.currentTimeMillis(); System.out.println("耗费时间="+(end-start)); } private static class Task implements Runnable{ private LongAdder counter; public Task(LongAdder counter){ this.counter = counter; } public void run() { for (int i = 0; i < 10000; i++) { counter.increment(); } } } }
AtomicLong每次自增都是需要flush和refersh
LongAdder,每个线程都会有自己的一个计数器,仅用来在自己的线程内计数,这样一来就不会和其他线程的计数器干扰
LongAdder只使用于统计求和的场景
适用于大的数据量计算,并且对计算顺序不要求
public class LongAccumulatorDemo { public static void main(String[] args) { //LongAccumulator()后面的参数为x,后面传递的参数为y LongAccumulator accumulator = new LongAccumulator((x, y) -> x + y, 0); accumulator.accumulate(1); accumulator.accumulate(2); System.out.println(accumulator.get()); } }
- CAS作用于并发,保证一些列操作是原子性
- 思想:我认为V的值应该是A,如果是的话那我就将它改成B,如果不是A(说明被别人修改过了),那我就不修改了,避免多人同时修改出错
- CAS有三个操作数:内存值V、预期值A、要修改的值B。当预期值A和内存值V相同时,才将内存值修改为B,否则什么都不做。最后返回现在的值
- CAS最终是利用CPU的指令来完成,CPU保证了原子性
CAS的等价代码:
public class SimulatedCAS { private volatile int value; //整个方法模拟cpu的一条指令,原子性 public synchronized int compareAndSwap(int expectedValue, int newValue){ int oldValue = value; if (oldValue == expectedValue){ value = newValue; } return oldValue; } }
使用两个线程来模拟多线程中CAS操作
public class TwoThreadCompetition implements Runnable{ private volatile int value; //整个方法模拟cpu的一条指令,原子性 public synchronized int compareAndSwap(int expectedValue, int newValue){ int oldValue = value; if (oldValue == expectedValue){ value = newValue; } return oldValue; } @Override public void run() { compareAndSwap(0, 1); } public static void main(String[] args) throws InterruptedException { TwoThreadCompetition r = new TwoThreadCompetition(); r.value = 0; Thread t1 = new Thread(r, "Thread1"); Thread t2 = new Thread(r, "Thread2"); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println(r.value); } }
典型的应用场景
- 乐观锁
- 并发容器
- 原子类
AtomicInteger中使用的源码
public class AtomicInteger extends Number implements java.io.Serializable { private static final long serialVersionUID = 6214790243416807050L; // setup to use Unsafe.compareAndSwapInt for updates //java无法访问直接访问 /unsafe 给我们提供了硬件级别的原子 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 getAndAddInt(Object var1, long var2, int var4) { //do while自旋,不停的尝试 int var5; do { var5 = this.getIntVolatile(var1, var2); } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4)); return var5; } }
CAS的缺点:
- ABA问题:他只是检查值是否相等,相等不代表没有被修改过。原来是5,可能A线程改为7,B线程改为5,对比后,发现还是5,但是值确实被修改过。解决方案:版本号
- 自旋时间可能过长,因为他是一个自旋,对性能有很多的消耗