Java --- JUC之原子类
创始人
2024-05-11 19:42:11
0

目录​​​​​​​

一、基本类型原子类

二、数组类型原子类

三、引用类型原子类

四、对象的属性修改类型原子类

五、原子操作增强类

5.1、高性能热点商品应用

5.2、LongAdder架构图

5.3、源码分析

一、基本类型原子类

public class AtomicTest1 {public static final int SIZE = 50;public static void main(String[] args) throws InterruptedException {MyAtomic myAtomic = new MyAtomic();CountDownLatch countDownLatch = new CountDownLatch(SIZE);for (int i = 1; i <= SIZE ; i++) {new Thread(()->{try {for (int j = 1; j <= 1000 ; j++) {myAtomic.add();}} finally {countDownLatch.countDown();}},String.valueOf(i)).start();}//等待五十个线程全部计算完,获取结果countDownLatch.await();System.out.println("计算结果为:"+myAtomic.atomicInteger.get());}
}
class MyAtomic{AtomicInteger atomicInteger = new AtomicInteger();public void add(){atomicInteger.getAndIncrement();}
}

二、数组类型原子类

public class AtomicTest2 {public static void main(String[] args) {AtomicIntegerArray array = new AtomicIntegerArray(new int[]{1, 2, 3, 4});for (int i = 0; i 

三、引用类型原子类

public class AtomicTest3 {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);try {TimeUnit.MILLISECONDS.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}markableReference.weakCompareAndSet(100, 101, marked, !marked);},"A").start();new Thread(()->{boolean marked = markableReference.isMarked();System.out.println(Thread.currentThread().getName() + "\t" + marked);try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}boolean b = markableReference.weakCompareAndSet(100, 102, marked, !marked);System.out.println(b+"\t"+Thread.currentThread().getName());System.out.println(Thread.currentThread().getName()+"\t"+markableReference.isMarked());System.out.println(Thread.currentThread().getName()+"\t"+markableReference.getReference());},"B").start();}
}

四、对象的属性修改类型原子类

1、使用目的:以一种线程安全的方式操作非线程安全对象内的某些字段

2、使用对象:①、更新对象属性必须使用public volatile修饰符。②、因为对象的属性修改类型原子类都是抽象类,所以每次使用都必须使用静态方法newUpdater()创建一个更新器,并且需要设置想要更新的类和属性。

public class AtomicTest4 {public static void main(String[] args) throws InterruptedException {Bank bank = new Bank();CountDownLatch countDownLatch = new CountDownLatch(10);for (int i = 1; i <=10 ; i++) {new Thread(()->{try {for (int j = 1; j <=1000 ; j++) {bank.add(bank);}} finally {countDownLatch.countDown();}},String.valueOf(i)).start();}countDownLatch.await();System.out.println(Thread.currentThread().getName() +"\t"+ bank.money);}
}
class Bank{String bankName = "NTM";public volatile int money = 0;AtomicIntegerFieldUpdater fieldUpdater = AtomicIntegerFieldUpdater.newUpdater(Bank.class,"money");public void add(Bank bank){fieldUpdater.getAndIncrement(bank);}
}

五、原子操作增强类

public class volatiles1 {public static void main(String[] args) {LongAdder longAdder = new LongAdder();longAdder.increment();longAdder.increment();longAdder.increment();System.out.println(longAdder.sum());//4LongAccumulator longAccumulator = new LongAccumulator((x,y) -> x + y,0);longAccumulator.accumulate(1);//1longAccumulator.accumulate(3);//4System.out.println(longAccumulator.get());}
}

LongAdder只能用来计算加法,且从零开始计算

LongAccumulato提供了自定义的函数操作。

5.1、高性能热点商品应用

public class volatiles2 {public static final int threadNum = 50;public static final int W = 10000;public static void main(String[] args) throws InterruptedException {long startTime;long endTime;Num num = new Num();CountDownLatch countDownLatch1 = new CountDownLatch(threadNum);CountDownLatch countDownLatch2 = new CountDownLatch(threadNum);CountDownLatch countDownLatch3 = new CountDownLatch(threadNum);CountDownLatch countDownLatch4 = new CountDownLatch(threadNum);startTime = System.currentTimeMillis();for (int i = 1; i <=threadNum ; i++) {new Thread(()->{try {for (int j = 1; j <= 100 * W ; j++) {num.getSynchronizedSum();}} finally {countDownLatch1.countDown();}},String.valueOf(i)).start();}countDownLatch1.await();endTime = System.currentTimeMillis();System.out.println("共花费:" + (endTime - startTime) + "毫秒\t" + num.num);startTime = System.currentTimeMillis();for (int i = 1; i <=threadNum ; i++) {new Thread(()->{try {for (int j = 1; j <= 100 * W ; j++) {num.getAtomicLongSum();}} finally {countDownLatch2.countDown();}},String.valueOf(i)).start();}countDownLatch2.await();endTime = System.currentTimeMillis();System.out.println("共花费:" + (endTime - startTime) + "毫秒\t" + num.getAtomicLong());startTime = System.currentTimeMillis();for (int i = 1; i <=threadNum ; i++) {new Thread(()->{try {for (int j = 1; j <= 100 * W ; j++) {num.getLongAdderSum();}} finally {countDownLatch3.countDown();}},String.valueOf(i)).start();}countDownLatch3.await();endTime = System.currentTimeMillis();System.out.println("共花费:" + (endTime - startTime) + "毫秒\t" + num.getLongAdder());startTime = System.currentTimeMillis();for (int i = 1; i <=threadNum ; i++) {new Thread(()->{try {for (int j = 1; j <= 100 * W ; j++) {num.getLongAccumulatorSum();}} finally {countDownLatch4.countDown();}},String.valueOf(i)).start();}countDownLatch4.await();endTime = System.currentTimeMillis();System.out.println("共花费:" + (endTime - startTime) + "毫秒\t" + num.getLongAccumulator());}
}
class Num{int num = 0;public synchronized void getSynchronizedSum(){num++;}AtomicLong atomicLong = new AtomicLong(0);public void getAtomicLongSum(){atomicLong.getAndIncrement();}public long getAtomicLong(){return atomicLong.get();}LongAdder longAdder = new LongAdder();public void getLongAdderSum(){longAdder.increment();}public long getLongAdder(){return longAdder.sum();}LongAccumulator longAccumulator = new LongAccumulator((x,y)->x+y,0);public void getLongAccumulatorSum(){longAccumulator.accumulate(1);}public long getLongAccumulator(){return longAccumulator.get();}
}

5.2、LongAdder架构图

 

 原理:Striped64中一些变量及方法

base:类似于AtomicLong中全局的value值。在没有竞争情况下数据直接累加到base上,或者calls扩容时,也需要将数据写入到base上

collide:表示扩容意向,false一定不会扩容,true可能会扩容。

cellsBusy:初始化cells或者扩容cells需要获取锁,0表示无锁状态,1:表示其他线程已经持有了锁。

casCellsBusy():通过CAS操作修改cellsBusy的值,CAS成功代表获取锁,返回true

NCPU:当前计算机CPU数量,Cell数组扩容时会使用到

getProbe();获取当前线程的hash值

advanceProbe():重置当前线程的hash值。

LongAdder的基本思路就是分散热点,将value值分散到到一个Call数组中,不同线程会命中到数组的不同槽中,各个线程只对自己槽中的那个值进行CAS操作,这样热点就被分散了,冲突的概率就小很多。如果要获取真正的long值,只要将各个槽中的变量值累加返回。

sum()会将所有Cell数组中的value和base累加作为返回值,核心的思想就是将之前AtomicLong一个value的更新压力分散到多个value中去,从而降低更新热点。

5.3、源码分析

 as:表示cells引用

b:表示获取的base值

v:表示cells数组的长度

m:表示cells数组的长度

a:表示当前线程命中的cell单元格

 public void add(long x) {Cell[] as; long b, v; int m; Cell a;//首次首线程(as = cells != null)一定是false,此时走casBase方法,以CAS的方式更新base值, 且只有当cas失败时,才会走到if中//条件1:cells不为空//条件2:cas操作base失败,说明其它线程先一步修改了base正在出现竞争if ((as = cells) != null || !casBase(b = base, b + x)) {//true无竞争  false表示竞争激烈,多个线程hash到同一个cell,可能要扩容boolean uncontended = true;//条件1:cells为空//条件2:应该不会出现//条件3:当前线程所在的cell为空,说明当前线程还没有更新过cell,应该初始化一个cell//条件4:更新当前线程所在的cell失败,说明现在竞争很激烈,多个线程hash到了同一个cell,                    应扩容if (as == null || (m = as.length - 1) < 0 ||//getProbe()方法返回线程的threadlocalRandomProbe字段//它是通过随机数生成的一个值,对于一个确定的线程这个值是固定的(a = as[getProbe() & m]) == null ||!(uncontended = a.cas(v = a.value, v + x)))longAccumulate(x, null, uncontended);//调用Striped64中的方法处理。}}

1、最初无竞争时只更新base

2、如果更新base失败后,首次新建一个Cell[]数组

3、当多个线程竞争同一个Cell比较激烈时,可能就要对Cell[]扩容。

longAccumulate(long x, LongBinaryOperator fn,boolean wasUncontended)

x:需要增加的值,一般默认都是1

fn:默认传递的是null

wasUncontended:竞争标识,如果是false则代表有竞争,只有cells初始化之后,并且当前线程CAS竞争修改失败,才会是false

    final void longAccumulate(long x, LongBinaryOperator fn,boolean wasUncontended) {//存储线程的probe值int h;//如果getProbe()返回0,说明随机数未初始化if ((h = getProbe()) == 0) {//使用ThreadLocalRandom为当前线程重新计算一个hash值,强制初始化ThreadLocalRandom.current(); // force initialization//重新获取prode值,hash值被重置就好比一个全新的线程一样,所以设置了wasUncontended竞争状态为trueh = getProbe();//重新计算了当前线程的hash后认为此次不算是一次竞争,都未初始化,肯定还不存在竞争激烈wasUncontended竞争状态为truewasUncontended = true;}//如果hash取模映射得到的Cell单元不是null,则为true,此值也可以看作是扩容意向boolean collide = false;                // True if last slot nonemptyfor (;;) {Cell[] as; Cell a; int n; long v;//cells已经被初始化了if ((as = cells) != null && (n = as.length) > 0) {if ((a = as[(n - 1) & h]) == null) {if (cellsBusy == 0) {       // Try to attach new CellCell r = new Cell(x);   // Optimistically createif (cellsBusy == 0 && casCellsBusy()) {boolean created = false;try {               // Recheck under lockCell[] rs; int m, j;if ((rs = cells) != null &&(m = rs.length) > 0 &&rs[j = (m - 1) & h] == null) {rs[j] = r;created = true;}} finally {cellsBusy = 0;}if (created)break;continue;           // Slot is now non-empty}}collide = false;}else if (!wasUncontended)       // CAS already known to failwasUncontended = true;      // Continue after rehashelse if (a.cas(v = a.value, ((fn == null) ? v + x :fn.applyAsLong(v, x))))break;else if (n >= NCPU || cells != as)collide = false;            // At max size or staleelse if (!collide)collide = true;else if (cellsBusy == 0 && casCellsBusy()) {try {if (cells == as) {      // Expand table unless staleCell[] rs = new Cell[n << 1];for (int i = 0; i < n; ++i)rs[i] = as[i];cells = rs;}} finally {cellsBusy = 0;}collide = false;continue;                   // Retry with expanded table}h = advanceProbe(h);}//cells没有加锁且没有初始化,则尝试对它进行加锁,并初始化cells数组else if (cellsBusy == 0 && cells == as && casCellsBusy()) {boolean init = false;try {                           // Initialize tableif (cells == as) {Cell[] rs = new Cell[2];rs[h & 1] = new Cell(x);cells = rs;init = true;}} finally {cellsBusy = 0;}if (init)break;}//cells正在进行初始化,则尝试直接在基数base上进行累加操作else if (casBase(v = base, ((fn == null) ? v + x :fn.applyAsLong(v, x))))break;                          // Fall back on using base}}
public long sum() {Cell[] as = cells; Cell a;long sum = base;if (as != null) {for (int i = 0; i < as.length; ++i) {if ((a = as[i]) != null)sum += a.value;}}return sum;
} 

sum方法将所有Cell数组中的value和base累加作为返回值

核心思想就是将之前AtomicLong一个value的更新压力分散到多个value中去,从而降级更新热点。

 

相关内容

热门资讯

常用商务英语口语   商务英语是以适应职场生活的语言要求为目的,内容涉及到商务活动的方方面面。下面是小编收集的常用商务...
六年级上册英语第一单元练习题   一、根据要求写单词。  1.dry(反义词)__________________  2.writ...
复活节英文怎么说 复活节英文怎么说?复活节的英语翻译是什么?复活节:Easter;"Easter,anniversar...
2008年北京奥运会主题曲 2008年北京奥运会(第29届夏季奥林匹克运动会),2008年8月8日到2008年8月24日在中华人...
英语道歉信 英语道歉信15篇  在日常生活中,道歉信的使用频率越来越高,通过道歉信,我们可以更好地解释事情发生的...
六年级英语专题训练(连词成句... 六年级英语专题训练(连词成句30题)  1. have,playhouse,many,I,toy,i...
上班迟到情况说明英语   每个人都或多或少的迟到过那么几次,因为各种原因,可能生病,可能因为交通堵车,可能是因为天气冷,有...
小学英语教学论文 小学英语教学论文范文  引导语:英语教育一直都是每个家长所器重的,那么有关小学英语教学论文要怎么写呢...
英语口语学习必看的方法技巧 英语口语学习必看的方法技巧如何才能说流利的英语? 说外语时,我们主要应做到四件事:理解、回答、提问、...
四级英语作文选:Birth ... 四级英语作文范文选:Birth controlSince the Chinese Governmen...
金融专业英语面试自我介绍 金融专业英语面试自我介绍3篇  金融专业的学生面试时,面试官要求用英语做自我介绍该怎么说。下面是小编...
我的李老师走了四年级英语日记... 我的李老师走了四年级英语日记带翻译  我上了五个学期的小学却换了六任老师,李老师是带我们班最长的语文...
小学三年级英语日记带翻译捡玉... 小学三年级英语日记带翻译捡玉米  今天,我和妈妈去外婆家,外婆家有刚剥的`玉米棒上带有玉米籽,好大的...
七年级英语优秀教学设计 七年级英语优秀教学设计  作为一位兢兢业业的人民教师,常常要写一份优秀的教学设计,教学设计是把教学原...
我的英语老师作文 我的英语老师作文(通用21篇)  在日常生活或是工作学习中,大家都有写作文的经历,对作文很是熟悉吧,...
英语老师教学经验总结 英语老师教学经验总结(通用19篇)  总结是指社会团体、企业单位和个人对某一阶段的学习、工作或其完成...
初一英语暑假作业答案 初一英语暑假作业答案  英语练习一(基础训练)第一题1.D2.H3.E4.F5.I6.A7.J8.C...
大学生的英语演讲稿 大学生的英语演讲稿范文(精选10篇)  使用正确的写作思路书写演讲稿会更加事半功倍。在现实社会中,越...
VOA美国之音英语学习网址 VOA美国之音英语学习推荐网址 美国之音网站已经成为语言学习最重要的资源站点,在互联网上还有若干网站...
商务英语期末试卷 Part I Term Translation (20%)Section A: Translate ...