系列文章目录
第一章 java JUC并发编程 Future: link
第二章 java JUC并发编程 多线程锁: link
第三章 java JUC并发编程 中断机制: link
第四章 java JUC并发编程 java内存模型JMM: link
第五章 java JUC并发编程 volatile与JMM: link
第六章 java JUC并发编程 CAS: link
第七章 java JUC并发编程 原子操作类增强: link
第八章 java JUC并发编程 ThreadLocal: link
第九章 java JUC并发编程 对象内存布局与对象头: link
第十章 java JUC并发编程 Synchronized与锁升级: link
第十一章 java JUC并发编程 AbstractQueuedSynchronizer之AQS: link
1 概述
java.util.concurrent.atomic 下的类
https://www.runoob.com/manual/jdk11api/java.base/java/util/concurrent/atomic/package-summary.html
2 分类说明
2.1 基本类型原子类
AtomicInteger,AtomicBoolean,AtomicLong
2.1.1 常用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)//获取当前的值,并加上预期的值
boolean compareAndSet(int expect,int update)//如果输入的数值等于预期值,则以原子方式将该值设置为输入值(update)
2.1.2 case(不推荐)
package com.atguigu.springcloud.util.interrup;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
class MyNumber2{
AtomicInteger atomicInteger = new AtomicInteger();
public void addPlusPlus(){
atomicInteger.getAndIncrement();
}
}
public class AtomicIntegerDemo {
public static final int SIZE=50;
public static void main(String[] args) throws InterruptedException{
MyNumber2 myNumber2 = new MyNumber2();
for (int i = 0; i < SIZE; i++) {
new Thread(()->{
for (int j = 1; j <= 1000; j++) {
myNumber2.addPlusPlus();
}
},String.valueOf(i)).start();
}
//等待50个线程计算完成后再获得最终值,否则在上面正在计算的时候main线程就去看结果,造成看到的结果不太准确。但是实际中不能这么写
try {TimeUnit.MILLISECONDS.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}
System.out.println(Thread.currentThread().getName()+"\t"+"result:"+myNumber2.atomicInteger.get());
}
}
2.1.3 CountDownLatch优化上面case
package com.atguigu.springcloud.util.interrup;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
class MyNumber2{
AtomicInteger atomicInteger = new AtomicInteger();
public void addPlusPlus(){
atomicInteger.getAndIncrement();
}
}
public class AtomicIntegerDemo {
public static final int SIZE=50;
public static void main(String[] args) throws InterruptedException{
MyNumber2 myNumber2 = new MyNumber2();
CountDownLatch countDownLatch = new CountDownLatch(SIZE);
for (int i = 0; i < SIZE; i++) {
new Thread(()->{
try {
for (int j = 1; j <= 1000; j++) {
myNumber2.addPlusPlus();
}
} finally {
//完成一个减一个
countDownLatch.countDown();
}
},String.valueOf(i)).start();
}
//等待50个线程计算完成后再获得最终值,否则在上面正在计算的时候main线程就去看结果,造成看到的结果不太准确。但是实际中不能这么写
// try {TimeUnit.MILLISECONDS.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}
//组织main线程获取数据,必须等到CountDownLatch的50个线程跑完
countDownLatch.await();
System.out.println(Thread.currentThread().getName()+"\t"+"result:"+myNumber2.atomicInteger.get());
}
}
2.2 数组类型原子类
AtomicIntegerArray,AtomicLongArray,AtomicReferenceArray
2.2.1 case
package com.atguigu.springcloud.util.interrup;
import java.util.concurrent.atomic.AtomicIntegerArray;
public class AtomicIntegerArrayDemo {
public static void main(String[] args) {
AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(new int[5]);
// AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(5);
// AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(new int[]{1,2,3,4,5});
for (int i = 0; i < atomicIntegerArray.length(); i++) {
System.out.println(atomicIntegerArray.get(i));
}
System.out.println();
int tmpInt = 0;
tmpInt = atomicIntegerArray.getAndSet(0,1122);
System.out.println(tmpInt+"\t"+atomicIntegerArray.get(0));
tmpInt = atomicIntegerArray.getAndIncrement(0);
System.out.println(tmpInt+"\t"+atomicIntegerArray.get(0));
}
}
2.3 引用类型原子类
2.3.1 AtomicReference,(前面的自旋锁Demo)
2.3.2 AtomicStampedReference(前面的ABADemo)
携带版本号的引用类型原子类,可以解决ABA问题
解决多次的问题
2.3.3 AtomicMarkableReference
原子更新是否带有标记为的引用类型对象
解决一次性的问题,是否修改过。它的定义就是将状态戳简化为true|false
package com.atguigu.springcloud.util.interrup;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicMarkableReference;
public class AtomicMarkableReferenceDemo {
static AtomicMarkableReference markableReference = new AtomicMarkableReference(100,false);
public static void main(String[] args) {
new Thread(()->{
boolean marked = markableReference.isMarked();
System.out.println(Thread.currentThread().getName()+"\t"+"默认标识"+"marked");
//等待t2线程和t1拿到一样的标识,都是false
try {TimeUnit.MILLISECONDS.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}
markableReference.compareAndSet(100,1000,marked,!marked);
},"t1").start();
new Thread(()->{
boolean marked = markableReference.isMarked();
System.out.println(Thread.currentThread().getName()+"\t"+"默认标识"+"marked");
try {TimeUnit.MILLISECONDS.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}
boolean b = markableReference.compareAndSet(100, 2000, marked, !marked);
System.out.println(Thread.currentThread().getName()+"\t"+"t2线程CASresult:"+b);
System.out.println(Thread.currentThread().getName()+"\t"+markableReference.isMarked());
System.out.println(Thread.currentThread().getName()+"\t"+markableReference.getReference());
},"t2").start();
}
}
2.4 对象的属性修改原子类
AtomicLntegerFieldUpdater:原子更新对象中int类型字段的值
AtomicLongFieldUpdater:原子更新对象中Long类型字段的值
AtomicReferenceFieldUpdater:原子更新引用类型字段的值
使用目的:以一种线程安全的方式操作非线程安全对象的某些字段
使用要求:
1.更新的对象属性必须使用public volatile修饰符
2.因为对象的属性修改类型原子类都是抽象类,所以每次使用都必须使用静态方法newUpdater()创建一个更新器,并且需要设置想要更新的类和属性
面试问题:你在哪里用了volatile?:AtomicReferenceFieldUpdater
2.4.1 AtomicIntegerFieldUpdaterDemo(针对字段是int类型的)
package com.atguigu.springcloud.util.interrup;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
class BankAccount{
String bankName="ccb";
//更新的对象属性必须使用public volatile修饰符
public volatile int money=0;//钱数
public synchronized void add(){
money++;
}
//因为对象的属性修改类型原子类都是抽象类,所以每次使用都必须
//使用静态方法newUpdater()创建一个更新器,并且需要设置想要更新的类和属性
AtomicIntegerFieldUpdater<BankAccount> fieldUpdater =AtomicIntegerFieldUpdater.newUpdater(BankAccount.class,"money");
//不加synchronized,保证高性能原子性,只影响到变量
public void transMoney(BankAccount bankAccount){
fieldUpdater.getAndIncrement(bankAccount);
}
}
/**
* 以一种线程安全的方式操作非线程安全对象的某些字段
* 需求:
* 10个线程
* 每个线程转账1000
* 不使用synchronized,尝试使用AtomicIntegerFieldUpdater来实现
*/
public class AtomicIntegerFieldUpdaterDemo {
public static void main(String[] args) throws InterruptedException {
BankAccount bankAccount = new BankAccount();
CountDownLatch countDownLatch = new CountDownLatch(10);
for (int i = 1; i <= 10; i++) {
new Thread(()->{
try {
for (int j = 1; j <=1000 ; j++) {
// bankAccount.add();
bankAccount.transMoney(bankAccount);
}
} finally {
countDownLatch.countDown();
}
},String.valueOf(i)).start();
}
countDownLatch.await();
System.out.println(Thread.currentThread().getName()+"\t"+"result:"+bankAccount.money);
}
}
2.4.2 AtomicReferenceFieldUpdater(其他类型的字段)
package com.atguigu.springcloud.util.interrup;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
class MyVar{
public volatile Boolean isInit = Boolean.FALSE;
AtomicReferenceFieldUpdater<MyVar,Boolean> referenceFieldUpdater = AtomicReferenceFieldUpdater.newUpdater(MyVar.class,Boolean.class,"isInit");
public void init(MyVar myVar){
if(referenceFieldUpdater.compareAndSet(myVar,Boolean.FALSE,Boolean.TRUE)){
System.out.println(Thread.currentThread().getName()+"\t"+"----start init,need 3seconds");
try {TimeUnit.MILLISECONDS.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}
System.out.println(Thread.currentThread().getName()+"\t"+"----over init");
}else{
System.out.println(Thread.currentThread().getName()+"\t"+"----已经有线程在进行初始化工作");
}
}
}
/**
* 需求:
* 多线程并发调用一个类的初始化方法,如果未被初始化过,将执行初始化工作,
* 要求只能被初始化一次,只有一个线程操作成功
*/
public class AtomicReferenceFieldUpdaterDemo {
public static void main(String[] args) {
MyVar myVar = new MyVar();
for (int i = 1; i <=5 ; i++) {
new Thread(()->{
myVar.init(myVar);
},String.valueOf(i)).start();
}
}
}
2.5 原子操作增强类原理深度解析
DoubleAccumulator,DoubleAdder,LongAccumulator,LongAdder
2.5.1 点赞计数器
常用API
LongAdder只能用来计算加法,并且从零开始计算
LongAccumulator提供了自定义的函数操作
LongAdderAPIDemo
package com.atguigu.springcloud.util.interrup;
import java.util.concurrent.atomic.LongAccumulator;
import java.util.concurrent.atomic.LongAdder;
import java.util.function.LongBinaryOperator;
public class LongAdderAPIDEmo {
public static void main(String[] args) {
LongAdder longAdder = new LongAdder();
longAdder.increment();
longAdder.increment();
longAdder.increment();
System.out.println(longAdder.sum());
/* LongAccumulator longAccumulator = new LongAccumulator(new LongBinaryOperator() {
@Override
public long applyAsLong(long left, long right) {
return left+right;
}
},0);//从0开始与下面的写法功能一样*/
LongAccumulator longAccumulator = new LongAccumulator((x,y)->x+y,0);
longAccumulator.accumulate(1);//1
longAccumulator.accumulate(3);//4
System.out.println(longAccumulator.get());
}
}
2.5.2 性能评估
package com.atguigu.springcloud.util.interrup;
import lombok.SneakyThrows;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAccumulator;
import java.util.concurrent.atomic.LongAdder;
class ClickNumber{
int number = 0;
//第一种传统 synchronized
public synchronized void clickBySynchronized(){
number++;
}
//第二种 AtomicLong
AtomicLong atomicLong = new AtomicLong(0);
public void clickByAtomicLong(){
atomicLong.getAndIncrement();
}
//第三种 LongAdder
LongAdder longAdder = new LongAdder();
public void clickByLongAdder(){
longAdder.increment();
}
//第四种 LongAccumulator
LongAccumulator longAccumulator = new LongAccumulator((x,y)->x+y,0);
public void clickByLongAccumulator(){
longAccumulator.accumulate(1);
}
}
/**
* 需求:50个线程,每个线程100w次,总点赞数出来
*/
public class AccumulatorCompareDemo {
public static final int _1w = 10000;
public static final int threadNumber=50;
@SneakyThrows
public static void main(String[] args) {
ClickNumber clickNumber = new ClickNumber();
long startTime;
long endTime;
CountDownLatch countDownLatch1 = new CountDownLatch(threadNumber);
CountDownLatch countDownLatch2 = new CountDownLatch(threadNumber);
CountDownLatch countDownLatch3 = new CountDownLatch(threadNumber);
CountDownLatch countDownLatch4 = new CountDownLatch(threadNumber);
startTime = System.currentTimeMillis();
for (int i = 1; i <=threadNumber ; i++) {
new Thread(()->{
try {
for (int j = 1; j <=100*_1w ; j++) {
clickNumber.clickBySynchronized();
}
} finally {
countDownLatch1.countDown();
}
},String.valueOf(i)).start();
}
countDownLatch1.await();
endTime = System.currentTimeMillis();
System.out.println("----costTime:"+(endTime-startTime)+"\t"+"毫秒clickBySynchronized"+clickNumber.number);
startTime = System.currentTimeMillis();
for (int i = 1; i <=threadNumber ; i++) {
new Thread(()->{
try {
for (int j = 1; j <=100*_1w ; j++) {
clickNumber.clickByAtomicLong();
}
} finally {
countDownLatch2.countDown();
}
},String.valueOf(i)).start();
}
countDownLatch2.await();
endTime = System.currentTimeMillis();
System.out.println("----costTime:"+(endTime-startTime)+"\t"+"毫秒clickByAtomicLong"+clickNumber.atomicLong.get());
startTime = System.currentTimeMillis();
for (int i = 1; i <=threadNumber ; i++) {
new Thread(()->{
try {
for (int j = 1; j <=100*_1w ; j++) {
clickNumber.clickByLongAdder();
}
} finally {
countDownLatch3.countDown();
}
},String.valueOf(i)).start();
}
countDownLatch3.await();
endTime = System.currentTimeMillis();
System.out.println("----costTime:"+(endTime-startTime)+"\t"+"clickByLongAdder"+clickNumber.longAdder.sum());
startTime = System.currentTimeMillis();
for (int i = 1; i <=threadNumber ; i++) {
new Thread(()->{
try {
for (int j = 1; j <=100*_1w ; j++) {
clickNumber.clickByLongAccumulator();
}
} finally {
countDownLatch4.countDown();
}
},String.valueOf(i)).start();
}
countDownLatch4.await();
endTime = System.currentTimeMillis();
System.out.println("----costTime:"+(endTime-startTime)+"\t"+"clickByLongAccumulator"+clickNumber.longAccumulator.get());
}
}
2.5.3 原理、源码分析
2.5.3.1 Striped64
Striped64有几个比较重要的成员函数
2.5.3.2 Striped64中一些变量或者方法的定义
2.5.3.3 Cell单元格类
是java.util.concurrent.atomic 下Striped64的一个内部类
2.5.3.4 LongAdder为什么这么快
2.5.3.5 源码分析
2.5.3.5.1 longAdder.increment()
add(L)
当线程变多的情况下第二个红框 !casBase(b = base, b+x)的cas操作就有可能没有抢到而返回了false,!false的情况下就进入了if逻辑块中,默认情况下uncontended变量为true(没有冲突)会进入longAccumulate(x,null,uncontended)方法。
278:新建Cells数组模式2大小
红色框代码(a=as[getProbe() & m])判断槽位是否有值
最后一个红框cas操作失败的情况了2槽位也顶不住就会继续执行longAccumulate扩容
2.5.3.5.2 longAccumulate
2.5.3.5.3 线程hash值:probe
2.5.3.5.4 总纲
阅读源码顺序简易2,3,1.
2.5.3.5.5 计算
CASE2
CASE3
CASE1
第一个if
第二个if
第三个if
第四个if
第五个if
第六个if
sum
sum()会将所有Cell数组中的value和base累加作为返回值。
核心的思想就是将之前AtomicLong一个value的更新压力分散到多个value中取,从而降低更新热点
3 总结
AtomicLong
线程安全,可允许一些性能损耗,要求高精度可使用
保证精度,性能代价
AtomicLong是多个线程针对单个热点值Value进行原子操作
LongAdder
当需要在高并发下有较好的性能表现,且对值得精确度要求不高时,可以使用
保证性能,精度代价
LongAdder是每个线程拥有自己的槽,各个线程一般只对自己槽中的那个值进行CAS操作