Atomic
基本类型
常用API
方法 |
解释 |
public final int get() |
获取当前的值 |
public final int getAndSet(int newValue) |
获取到当前的值,并设置新的值 |
public final int getAndIncrement() |
获取当前的值,并自增 |
public final int getAndDecrement() |
获取到当前的值,并自减 |
public final int getAndAdd(int delta) |
获取到当前的值,并加上预期的值 |
public final int incrementAndGet( ) |
返回的是加1后的值 |
boolean compareAndSet(int expect,int update) |
如果输入的数值等于预期值,返回true |
AtomicInteger 解决 i++
CountDownLatch 如何在程序中使用
在没使用 CountDownLatch 时
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| package tech.chen.juccode.a14;
import java.util.concurrent.atomic.AtomicInteger;
public class CountDownDemo { private static AtomicInteger atomicInteger = new AtomicInteger(0); public static void main(String[] args) { for (int i = 0; i < 50; i++) { new Thread(()->{ for (int j = 0; j < 100; j++) { atomicInteger.getAndIncrement(); } }).start(); } System.out.println("atomicInteger.get() = " + atomicInteger.get()); } }
|
结果:
atomicInteger.get() = 4900
发现使用 AtomicInteger 居然没有得到理想值 5000,是 AtomicInteger 的问题吗?显然不可能,实际原因是,累加还没结束,主线程就打印出了结果,不信你可以等两秒。但是,实际操作我们不可能设置一个固定的睡眠时间。CountDownLatch 出现的意义也在此。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| package tech.chen.juccode.a14;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger;
public class CountDownDemo { private static AtomicInteger atomicInteger = new AtomicInteger(0); private static CountDownLatch countDownLatch = new CountDownLatch(50); public static void main(String[] args) { for (int i = 0; i <50; i++) { new Thread(()->{ for (int j = 0; j < 100; j++) { atomicInteger.getAndIncrement(); } countDownLatch.countDown(); }).start(); } try { countDownLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("atomicInteger.get() = " + atomicInteger.get()); } }
|
atomicInteger.get() = 5000
AtomicBoolean中断标识
AtomicBoolean可以作为中断标识停止线程的方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public class AtomicBooleanDemo { public static void main(String[] args) { AtomicBoolean atomicBoolean=new AtomicBoolean(false);
new Thread(()->{ System.out.println(Thread.currentThread().getName()+"\t"+"coming....."); while(!atomicBoolean.get()){ System.out.println("=========="); } System.out.println(Thread.currentThread().getName()+"\t"+"over....."); },"A").start();
new Thread(()->{ atomicBoolean.set(true); },"B").start(); } }
|
AtomicLong
AtomicLong 的底层是 CAS+自旋锁 的思想,适用于低并发的全局计算,高并发后性能急剧下降,原因如下:N个线程CAS操作修改线程的值,每次只有一个成功过,其他N-1失败,失败的不停的自旋直到成功,这样大量失败自旋的情况,一下子 cpu 就打高了(AtomicLong的自旋会成为瓶颈)
在高并发的情况下,我们使用LoadAdder
数组类型
AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray
代码演示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package tech.chen.juccode.a14;
import java.util.concurrent.atomic.AtomicIntegerArray;
public class AtomicArray { public static void main(String[] args) { AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(5); for (int i = 0; i < atomicIntegerArray.length(); i++) { int i1 = atomicIntegerArray.get(i); System.out.println("i1 = " + i1); }
int i = atomicIntegerArray.incrementAndGet(0); System.out.println("i = " + i); int andAdd = atomicIntegerArray.addAndGet(1, 4); System.out.println("andAdd = " + andAdd); } }
|
引用类型
AtomicReference、AtomicStampedReference、AtomicMarkableReference
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| package tech.chen.juccode.a14; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference;
public class AtomicReferenceForLock {
private static AtomicReference<Thread> atomicReference = new AtomicReference<>();
private static void lock(){ Thread thread = Thread.currentThread(); System.out.println(Thread.currentThread().getName() + "\t" + "coming "); while (!atomicReference.compareAndSet(null, thread)) {} }
private static void unlock(){ Thread thread = Thread.currentThread(); System.out.println(Thread.currentThread().getName() + "\t" + "take over "); atomicReference.compareAndSet(thread,null); }
public static void main(String[] args) { new Thread(()->{ lock(); try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } unlock(); },"t1").start(); new Thread(()->{ lock(); unlock(); },"t2").start(); } }
|
AtomicStampedReference 这个带有戳记的 引用原子类 在上一篇已经讲过,这里就不赘述了
AtomicMarkableReference
- 原子更新带有标志位的引用类型对象
- 解决是否修改(它的定义就是将状态戳简化位 true|false ) ,类似一次性筷子
- 状态戳 (true/false) 原子引用
- 不建议用它解决 ABA 问题
- Markable – true、false 是否修改过,跟 AtomicStampedReference 的区别就是 它只负责监督一次改变。
对象属性修改类型
目的 : 以一种线程安全的方式操作非线程安全对象内的某些字段 。判断是否可以不要锁定整个对象,减少锁定的范围,只关注长期、敏感性变化的某一个字段,而不是整个对象,达到精确加锁+节约内存的目的
条件:
- 更新的对象属性必须使用 public volatile 修饰符
- 因为对象的属性修改类型原子类都是抽象类 ,所以每次使用都必须使用静态方法 newUpdater( ) 创建一个更新器,并且需要设置想要更新的类和属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| package tech.chen.juccode.a14;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
public class AtomicFieldUpdaterDemo { public static void main(String[] args) { User user = new User(); AtomicIntegerFieldUpdater<User> atomicIntegerFieldUpdaterAge = AtomicIntegerFieldUpdater.newUpdater(User.class,"age"); AtomicReferenceFieldUpdater atomicReferenceFieldUpdaterName = AtomicReferenceFieldUpdater.newUpdater(User.class,String.class,"name"); int i = atomicIntegerFieldUpdaterAge.get(user); System.out.println("i = " + i); Object o = atomicReferenceFieldUpdaterName.get(user); System.out.println("o = " + o); int newAge = atomicIntegerFieldUpdaterAge.accumulateAndGet(user, 18, (x, y) -> x + y); System.out.println("newAge = " + newAge); boolean czk = atomicReferenceFieldUpdaterName.compareAndSet(user, "czk", "0205"); System.out.println("czk = " + czk); } } class User{ public volatile int age = 0; public volatile String name = "czk"; }
|
运行结果
i = 0
o = czk
newAge = 18
原子操作增强类
DoubleAccumulator 、DoubleAdder 、LongAccumulator 、LongAdder
常用API
方法 |
解释 |
void add(long x) |
将当前的value加1 |
void increment( ) |
将当前的value加1 |
void decrement( ) |
将当前value减1 |
long sum( ) |
返回当前的值,特别注意,在没有并发更新value的情况下sum会返回一个精确值,在存在并发的情况下,sum不保证返回精确值 |
long longvale |
等价于long sum( )。将value重置为0,可用于替换重新new一个LongAdder,但此方法只可以在没有并发更新的情况下使用 |
long sumThenReset() |
获取当前value,并将value重置为0 |
- LongAdder只能用来计算加法、减法,且从零开始计算
- LongAccumulator提供了自定义的函数操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
| package tech.chen.juccode.a14;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.LongAccumulator; import java.util.concurrent.atomic.LongAdder;
public class LongAdderCalcDemo { public static final int SIZE_THREAD=50; public static final int _1w=10000;
public static void main(String[] args) { synchronizedTest(); atomicIntegerTest(); atomicLongTest(); longAdderTest(); longAccumulatorTest(); }
private static void longAdderTest() { CountDownLatch countDownLatch=new CountDownLatch(SIZE_THREAD); ClickNumber clickNumber=new ClickNumber(); long startTime = System.currentTimeMillis(); for (int i = 1 ; i <=SIZE_THREAD ; i++) { new Thread(()->{ try{ for (int j = 1; j <=100*_1w; j++) { clickNumber.add_longAdder(); } }finally { countDownLatch.countDown(); } },String.valueOf(i)).start(); } try { countDownLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); } long endTime = System.currentTimeMillis(); System.out.print("-----add_longAdder:"+(endTime-startTime)+"毫秒"+"\t"); System.out.println(clickNumber.adder.longValue()); } private static void synchronizedTest() { CountDownLatch countDownLatch=new CountDownLatch(SIZE_THREAD); ClickNumber clickNumber=new ClickNumber(); long startTime = System.currentTimeMillis(); for (int i = 1 ; i <=SIZE_THREAD ; i++) { new Thread(()->{ try{ for (int j = 1; j <=100*_1w; j++) { clickNumber.add_synchronized(); } }finally { countDownLatch.countDown(); } },String.valueOf(i)).start(); } try { countDownLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); } long endTime = System.currentTimeMillis(); System.out.print("-----add_synchronized:"+(endTime-startTime)+"毫秒"+"\t"); System.out.println(clickNumber.number); } private static void atomicIntegerTest() { CountDownLatch countDownLatch=new CountDownLatch(SIZE_THREAD); ClickNumber clickNumber=new ClickNumber(); long startTime = System.currentTimeMillis(); for (int i = 1 ; i <=SIZE_THREAD ; i++) { new Thread(()->{ try{ for (int j = 1; j <=100*_1w; j++) { clickNumber.add_atomicInteger(); } }finally { countDownLatch.countDown(); } },String.valueOf(i)).start(); } try { countDownLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); } long endTime = System.currentTimeMillis(); System.out.print("-----add_atomicInteger:"+(endTime-startTime)+"毫秒"+"\t"); System.out.println(clickNumber.atomicInteger.get()); } private static void atomicLongTest() { CountDownLatch countDownLatch=new CountDownLatch(SIZE_THREAD); ClickNumber clickNumber=new ClickNumber(); long startTime = System.currentTimeMillis(); for (int i = 1 ; i <=SIZE_THREAD ; i++) { new Thread(()->{ try{ for (int j = 1; j <=100*_1w; j++) { clickNumber.add_atomicLong(); } }finally { countDownLatch.countDown(); } },String.valueOf(i)).start(); } try { countDownLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); } long endTime = System.currentTimeMillis(); System.out.print("-----add_atomicLong:"+(endTime-startTime)+"毫秒"+"\t"); System.out.println(clickNumber.atomicLong.get()); } private static void longAccumulatorTest() { CountDownLatch countDownLatch=new CountDownLatch(SIZE_THREAD); ClickNumber clickNumber=new ClickNumber(); long startTime = System.currentTimeMillis(); for (int i = 1 ; i <=SIZE_THREAD ; i++) { new Thread(()->{ try{ for (int j = 1; j <=100*_1w; j++) { clickNumber.add_longAccumulater(); } }finally { countDownLatch.countDown(); } },String.valueOf(i)).start(); } try { countDownLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); } long endTime = System.currentTimeMillis(); System.out.print("-----add_longAccumulater:"+(endTime-startTime)+"毫秒"+"\t"); System.out.println(clickNumber.accumulator.get()); } } class ClickNumber{ int number=0;
public synchronized void add_synchronized(){ number++; } AtomicInteger atomicInteger=new AtomicInteger(); public void add_atomicInteger(){ atomicInteger.incrementAndGet(); } AtomicLong atomicLong=new AtomicLong(); public void add_atomicLong(){ atomicLong.incrementAndGet(); } LongAdder adder=new LongAdder(); public void add_longAdder(){ adder.increment(); } LongAccumulator accumulator=new LongAccumulator((x, y)->x+y,0); public void add_longAccumulater(){ accumulator.accumulate(1); } }
|
运行结果:
—–add_synchronized:4089毫秒 50000000
—–add_atomicInteger:822毫秒 50000000
—–add_atomicLong:831毫秒 50000000
—–add_longAdder:187毫秒 50000000
—–add_longAccumulater:123毫秒 50000000