Java并发学习总结:原子操作类

本文是学习尚硅谷周阳老师《JUC并发编程》的总结(文末有链接)。

基本类型原子类

  • AtomicInteger
  • AtomicLong
  • AtomicBoolean

AtomicInteger 的方法 getAndIncrement 和 incrementAndGet 的区别:

两个方法都能实现对当前值加 1 , 但返回值不同,getAndIncrement 方法返回加 1 前的旧值,incrementAndGet 方法返回加 1 后的新值。

package juc.cas;import java.util.concurrent.atomic.AtomicInteger;public class ShareNumCas {private AtomicInteger num = new AtomicInteger(0);public int add() {return num.getAndIncrement();}public int add2() {return num.incrementAndGet();}public int getNum() {return num.get();}
}public class NumPlusDemo {public static void main(String[] args) {ShareNumCas share = new ShareNumCas();System.out.println("before add num is " + share.getNum());System.out.println("add return " + share.add());System.out.println("after add num is " + share.getNum());}
}public class NumPlusDemo2 {public static void main(String[] args) {ShareNumCas share = new ShareNumCas();System.out.println("before add num is " + share.getNum());System.out.println("add2 return " + share.add2());System.out.println("after add num is " + share.getNum());}
}

输出:

before add num is 0
add return 0
after add num is 1before add num is 0
add2 return 1
after add num is 1

数组类型原子类

  • AtomicIntegerArray
  • AtomicLongArray
  • AtomicReferenceArray

示例代码:

package juc.atomic;import java.util.concurrent.atomic.AtomicIntegerArray;public class AtomicIntegerArrayDemo {public static void main(String[] args) {AtomicIntegerArray array = new AtomicIntegerArray(5);// AtomicIntegerArray array = new AtomicIntegerArray(new int[]{1, 2, 3, 4, 5});for (int i = 0; i < array.length(); i++) {System.out.println(array.get(i));}System.out.println();int tmp = array.getAndSet(0, 100);System.out.println(tmp + " " + array.get(0));tmp = array.getAndIncrement(0);System.out.println(tmp + " " + array.get(0));}
}

输出:

0
0
0
0
00 100
100 101

引用类型原子类

  • AtomicReference
  • AtomicStampedReference
  • AtomicMarkableReference

AtomicReference 实现自旋锁

package juc.cas;import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;public class SpinLockDemo {AtomicReference<Thread> atomicReference = new AtomicReference<>();public void lock() {Thread thread = Thread.currentThread();System.out.println(thread.getName() + " come in");while (!atomicReference.compareAndSet(null, thread)) {}}public void unlock() {Thread thread = Thread.currentThread();atomicReference.compareAndSet(thread, null);System.out.println(thread.getName() + " task over, unlock");}public static void main(String[] args) {SpinLockDemo spinLockDemo = new SpinLockDemo();new Thread(() -> {spinLockDemo.lock();try {TimeUnit.SECONDS.sleep(5);} catch (InterruptedException e) {throw new RuntimeException(e);}spinLockDemo.unlock();}, "A").start();try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {throw new RuntimeException(e);}new Thread(() -> {spinLockDemo.lock();spinLockDemo.unlock();}, "B").start();}
}

输出:

A come in
B come in
A task over, unlock
B task over, unlock

AtomicStampedReference VS AtomicMarkableReference

AtomicStampedReference 可以记录对象被修改过几次

AtomicMarkableReference 只能记录对象是否被修改过

AtomicMarkableReference 示例代码:

package juc.atomic;import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicMarkableReference;public class AtomicMarkableReferenceDemo {static AtomicMarkableReference<Integer> atomicMarkableReference = new AtomicMarkableReference<>(100, false);public static void main(String[] args) {new Thread(() -> {boolean marked = atomicMarkableReference.isMarked();System.out.println(Thread.currentThread().getName() + " init marked = " + marked);try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {throw new RuntimeException(e);}boolean result = atomicMarkableReference.compareAndSet(100, 101, marked, !marked);System.out.println(Thread.currentThread().getName() + " result = " + result +", marked = " + atomicMarkableReference.isMarked() + ", value = " + atomicMarkableReference.getReference());}, "t1").start();new Thread(() -> {boolean marked = atomicMarkableReference.isMarked();System.out.println(Thread.currentThread().getName() + " init marked = " + marked);try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {throw new RuntimeException(e);}boolean result = atomicMarkableReference.compareAndSet(100, 102, marked, !marked);System.out.println(Thread.currentThread().getName() + " result = " + result+ ", marked = " + atomicMarkableReference.isMarked() + ", value = " + atomicMarkableReference.getReference());}, "t2").start();}
}

输出:

t2 init marked = false
t1 init marked = false
t1 result = true, marked = true, value = 101
t2 result = false, marked = true, value = 101

对象的属性修改原子类

  • AtomicIntegerFieldUpdater
  • AtomicLongFieldUpdater
  • AtomicReferenceFieldUpdater<T,V>

下例中可以将 money 定义为 AtomicInteger,已经有了 AtomicInteger ,AtomicIntegerFieldUpdater 有什么使用场景?

AtomicIntegerFieldUpdater 示例代码:

package juc.atomic;import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;class BankAccount {String bankName = "CCB";public volatile int money = 0;AtomicIntegerFieldUpdater<BankAccount> updater =AtomicIntegerFieldUpdater.newUpdater(BankAccount.class, "money");public void add(BankAccount bankAccount) {updater.getAndIncrement(bankAccount);}
}
public class AtomicIntegerFieldUpdaterDemo {public static void main(String[] args) throws InterruptedException {BankAccount bankAccount = new BankAccount();CountDownLatch countDownLatch = new CountDownLatch(10);for (int i = 0; i < 10; i++) {new Thread(() -> {for (int j = 0; j < 1000; j++) {bankAccount.add(bankAccount);}countDownLatch.countDown();}).start();}countDownLatch.await();System.out.println("after add money is " + bankAccount.money);}
}

输出:

after add money is 10000

AtomicReferenceFieldUpdater<T,V> 示例代码:

package juc.atomic;import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;class MyVar {public volatile Boolean isInit = Boolean.FALSE;AtomicReferenceFieldUpdater<MyVar, Boolean> updater =AtomicReferenceFieldUpdater.newUpdater(MyVar.class, Boolean.class, "isInit");public void init(MyVar myVar) {if(updater.compareAndSet(myVar, Boolean.FALSE, Boolean.TRUE)) {System.out.println(Thread.currentThread().getName() + " start init");try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(Thread.currentThread().getName() + " init success");} else {System.out.println(Thread.currentThread().getName() + " other thread is initing...");}}
}public class AtomicReferenceFieldUpdaterDemo {public static void main(String[] args) {MyVar myVar = new MyVar();for (int i = 0; i < 5; i++) {new Thread(() -> myVar.init(myVar), "t" + i).start();}}
}

输出:

t3 other thread is initing...
t1 other thread is initing...
t4 other thread is initing...
t2 other thread is initing...
t0 start init
t0 init success

原子操作增强类

  • LongAdder
  • LongAccumulator
  • DoubleAdder
  • DoubleAccumulator

LongAdder VS LongAccumulator

  • LongAdder 的初始值只能为 0
  • LongAdder 只能进行加减操作
  • LongAccumulator 没有上面两个限制,更灵活

LongAdder#sum() 不会加锁,不会影响对数的操作。

LongAdder 简单示例:

package juc.atomic;import java.util.concurrent.atomic.LongAdder;public class LongAdderDemo {public static void main(String[] args) {LongAdder longAdder = new LongAdder();longAdder.increment();longAdder.increment();longAdder.increment();longAdder.increment();longAdder.increment();System.out.println(longAdder.sum());}
}

输出:

5

LongAccumulator 简单示例:

package juc.atomic;import java.util.concurrent.atomic.LongAccumulator;public class LongAccumulatorDemo {public static void main(String[] args) {LongAccumulator longAccumulator = new LongAccumulator((x, y) -> x + y, 0);// 可以使用方法引用简写// LongAccumulator longAccumulator = new LongAccumulator(Long::sum, 0);longAccumulator.accumulate(1);System.out.println(longAccumulator.get());longAccumulator.accumulate(2);System.out.println(longAccumulator.get());}
}

输出:

1
3

各种计数性能比较

package juc.atomic;import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAccumulator;
import java.util.concurrent.atomic.LongAdder;class ClickNum {int number = 0;public synchronized void add() {number++;}AtomicLong atomicLong = new AtomicLong(0);public void addAtomic() {atomicLong.getAndIncrement();}LongAdder longAdder = new LongAdder();public void addLongAdder() {longAdder.increment();}LongAccumulator longAccumulator = new LongAccumulator((x, y) -> x + y, 0);public void addLongAccumulator() {longAccumulator.accumulate(1);}
}public class CountPerfTester {private static final int THREADS_COUNT = 50;private static final int TEN_THOUSANDS = 10000;public static void main(String[] args) throws InterruptedException {ClickNum clickNum = new ClickNum();long start = 0L;long end = 0L;CountDownLatch countDownLatch1 = new CountDownLatch(THREADS_COUNT);CountDownLatch countDownLatch2 = new CountDownLatch(THREADS_COUNT);CountDownLatch countDownLatch3 = new CountDownLatch(THREADS_COUNT);CountDownLatch countDownLatch4 = new CountDownLatch(THREADS_COUNT);start = System.currentTimeMillis();for (int i = 0; i < THREADS_COUNT; i++) {new Thread(() -> {try {for (int j = 1; j <= 100 * TEN_THOUSANDS; j++) {clickNum.add();}} finally {countDownLatch1.countDown();}}).start();}countDownLatch1.await();end = System.currentTimeMillis();System.out.println("synchronized add cost time : " + (end - start) + " ms, " + "result is " + clickNum.number);start = System.currentTimeMillis();for (int i = 0; i < THREADS_COUNT; i++) {new Thread(() -> {try {for (int j = 1; j <= 100 * TEN_THOUSANDS; j++) {clickNum.addAtomic();}} finally {countDownLatch2.countDown();}}).start();}countDownLatch2.await();end = System.currentTimeMillis();System.out.println("addAtomic cost time : " + (end - start) + " ms, " + "result is " + clickNum.atomicLong.get());start = System.currentTimeMillis();for (int i = 0; i < THREADS_COUNT; i++) {new Thread(() -> {try {for (int j = 1; j <= 100 * TEN_THOUSANDS; j++) {clickNum.addLongAdder();}} finally {countDownLatch3.countDown();}}).start();}countDownLatch3.await();end = System.currentTimeMillis();System.out.println("addLongAdder cost time : " + (end - start) + " ms, " + "result is " + clickNum.longAdder.sum());start = System.currentTimeMillis();for (int i = 0; i < THREADS_COUNT; i++) {new Thread(() -> {try {for (int j = 1; j <= 100 * TEN_THOUSANDS; j++) {clickNum.addLongAccumulator();}} finally {countDownLatch4.countDown();}}).start();}countDownLatch4.await();end = System.currentTimeMillis();System.out.println("addLongAccumulator cost time : " + (end - start) + " ms, " + "result is " + clickNum.longAccumulator.get());}
}

输出:

synchronized add cost time : 1352 ms, result is 50000000
addAtomic cost time : 844 ms, result is 50000000
addLongAdder cost time : 97 ms, result is 50000000
addLongAccumulator cost time : 74 ms, result is 50000000

可以看出:

LongAdder 和 LongAccumulator 的时间是 synchronized 和 AtomicLong 的十分之一左右。

LongAdder 高性能原理说明

LongAdder 的基本思路是分散热点,将 value 值分散到一个 Cell 数组中,不同的线程会命中到不同的数组元素中,各个线程只对自己数组元素的 value 进行 CAS 操作,冲突概率会小很多,sum() 方法会对 base 和所有 Cell 数组的值累加作为返回值。

某个线程分配到哪个 Cell 的方法是:对线程 id 进行 hash 求值,再根据 hash 值映射到 Cell 数组的某个下标。

Cell 数组元素是 2 的 n 次方,最大是 CPU 的核数。

LongAdder 源码分析

add(1L)

  • 最初无竞争时只更新 base
  • 如果更新 base 失败时,首次新增一个 Cell 数组
  • 当多个线程对同一个 Cell 竞争激烈时,可能就会对 Cell 数组扩容
    public void add(long x) {Cell[] cs; long b, v; int m; Cell c;if ((cs = cells) != null || !casBase(b = base, b + x)) {int index = getProbe();boolean uncontended = true;if (cs == null || (m = cs.length - 1) < 0 ||(c = cs[index & m]) == null ||!(uncontended = c.cas(v = c.value, v + x)))longAccumulate(x, null, uncontended, index);}}

判断代码中有赋值,也有方法调用,简化了代码。

longAccumulator

分为 3 种情况:

  • Cell 数组已经初始化

     这种情况下主要进行对某个 Cell 元素的初始化赋值,在某个 Cell 元素上重试 CAS,对 Cell 数组扩容
    
  • Cell 数组未初始化

     这种情况下首次创建 Cell 数组,包含 2 个元素
    
  • Cell 数组正在初始化,则尝试在 base 上重试 CAS 操作

sum

    /*** Returns the current sum.  The returned value is <em>NOT</em> an* atomic snapshot; invocation in the absence of concurrent* updates returns an accurate result, but concurrent updates that* occur while the sum is being calculated might not be* incorporated.** @return the sum*/public long sum() {Cell[] cs = cells;long sum = base;if (cs != null) {for (Cell c : cs)if (c != null)sum += c.value;}return sum;}

sum 会将所有 Cell 数组中的 value 和 base 累加作为返回值,sum 执行时并没有限制对 base 和 Cells 的值更新,所以返回值可能会损失一些精度。LongAdder 不是强一致性的,是最终一致性的。

参考

  • 尚硅谷JUC并发编程 (https://www.bilibili.com/video/BV1ar4y1x727?spm_id_from=333.788.videopod.episodes&vd_source=9266b9af652d5902d068c94b9d60116f&p=80)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/57952.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

家用wifi的ip地址固定吗?换wifi就是换ip地址吗

在探讨家用WiFi的IP地址是否固定&#xff0c;以及换WiFi是否就意味着换IP地址这两个问题时&#xff0c;我们首先需要明确几个关键概念&#xff1a;IP地址、家用WiFi网络、以及它们之间的相互作用。 一、家用WiFi的IP地址固定性 家用WiFi环境中的IP地址通常涉及两类&#xff1a…

图解:什么是多租户?

大家好&#xff0c;我是汤师爷~ 什么是多租户&#xff1f; 多租户是SaaS&#xff08;软件即服务&#xff09;领域里特有的一个概念。在SaaS服务中&#xff0c;“租户”指的就是使用这个SaaS系统的客户。 那么租户和用户有什么区别呢&#xff1f;举个例子。假设你正在使用一款…

从零开始的Go语言之旅(2 Go by Example: Values)

Go 语言有多种值类型&#xff0c;包括字符串、整数、浮点数、布尔值等。以下是一些基本示例。 package mainimport "fmt"func main() {fmt.Println("go" "lang")fmt.Println("11 ", 11)fmt.Println("7.0/3.0 ", 7.0/3.0)f…

[计算机网络]第一周

TCP/IP 与OSI TCP/IP TCP/IP 四层模型是一个分层网络通信模型&#xff0c;它将网络通信过程分为四个层次&#xff0c;这四层分别是&#xff1a;网络接口层、互联网层、传输层和应用层。 网络接口层负责在计算机和网络硬件之间传输数据&#xff0c;负责在物理网络上发送和接收…

六,Linux基础环境搭建(CentOS7)- 安装HBase

Linux基础环境搭建&#xff08;CentOS7&#xff09;- 安装HBase 大家注意以下的环境搭建版本号&#xff0c;如果版本不匹配有可能出现问题&#xff01; 一、HBase下载及安装 HBase是一个分布式的、面向列的开源数据库&#xff0c;该技术来源于 Fay Chang 所撰写的Google论文“…

AndroidStudio部署多渠道打包环境(一)

对于游戏来说&#xff0c;需要上架国内很多家应用商店&#xff0c;还有一些小的渠道SDK&#xff0c;大大小小加起来也有几十家了&#xff0c;那么我们部署了多渠道打包环境之后就很方便了。 一 、配置游戏基本参数&#xff1a;在app下面的build.gradle文件里编辑&#xff0c; …

基于Python+Django的气象数据分析与可视化系统

前言 随着互联网技术不断地发展&#xff0c;网络与大数据成为了人们生活的一部分&#xff0c;而气象数据分析与可视化系统 作为网上应用的一个全新的体现&#xff0c;由于其特有的便捷性&#xff0c;已经被人们所接受 本系统采用的框架为Django和python开发了气象数据分析与可…

基于SSM轻型卡车零部件销售系统的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;配件类型管理&#xff0c;配件信息管理&#xff0c;订单信息管理&#xff0c;检修休息管理&#xff0c;系统管理 用户账号功能包括&#xff1a;系统首页&#xff0c;个人中心&…

arm架构 ubuntu 部署docker

如果有旧版本需要卸载 sudo apt remove docker docker-engine docker-ce docker.io 安装依赖包 sudo apt update && apt install -y apt-transport-https ca-certificates curl software-properties-common 添加docker秘钥 阿里云 curl -fsSL http://mirrors.aliyu…

Java | Leetcode Java题解之第501题二叉搜索树中的众数

题目&#xff1a; 题解&#xff1a; class Solution {int base, count, maxCount;List<Integer> answer new ArrayList<Integer>();public int[] findMode(TreeNode root) {TreeNode cur root, pre null;while (cur ! null) {if (cur.left null) {update(cur.…

基于SpringCloud的WMS管理系统源码

商品管理&#xff1a;商品类型&#xff0c;规格&#xff0c;详情等设置。 采购管理&#xff1a;采购单录入。 销售管理&#xff1a;销售单录入。 库存管理&#xff1a;库存查询、库存日志 采用前后端分离的模式&#xff0c;微服务版本前端 后端采用Spring Boot、Spring Cl…

前端零基础入门到上班:【Day1】什么是前端?

本来打算开付费专栏 但是想起那句话 赠人玫瑰手留余香 引言1. 什么是前端&#xff1f;1.1 前端的定义1.2 前端的三大核心技术1.3 前端框架和工具 2. 什么是后端&#xff1f;2.1 后端的定义2.2 后端的组成要素2.3 后端框架和工具 3. 前后端的区别4. 什么是前后端分离&#xff1f…

MySQL 复合索引测试

对MySQL复合索引结合具体示例&#xff0c;各条件下索引使用情况的运行结果及分析。 目录 复合索引示例 创建表 新增数据 查询数据 选项A SQL查询 explain分析 选项B SQL查询 explain分析 选项C SQL查询 explain分析 选项D SQL查询 explain分析 选项E SQL查询…

音质好的骨传导耳机有哪些?音质最好的骨传导耳机推荐

最近发现市场上骨传导耳机的品牌和型号琳琅满目&#xff0c;小伙伴们在选择时可能会面临一些困惑。特别对于喜欢户外活动或运动的朋友来说&#xff0c;选对骨传导耳机很有必要&#xff0c;因为这能够让运动者在运动中享受音乐的同时&#xff0c;不必担心周围环境的变化&#xf…

Linux Shell 实现一键部署mariadb11.6

mariadb MariaDB数据库管理系统是MySQL的一个分支,主要由开源社区在维护,采用GPL授权许可 MariaDB的目的是完全兼容MySQL,包括API和命令行,使之能轻松成为MySQL的代替品。在存储引擎方面,使用XtraDB来代替MySQL的InnoDB。 MariaDB由MySQL的创始人Michael Widenius主导开发…

入侵检测算法平台部署LiteAIServer视频智能分析平台行人入侵检测算法

在当今科技日新月异的时代&#xff0c;行人入侵检测技术作为安全防护的重要组成部分&#xff0c;正经历着前所未有的发展。入侵检测算法平台部署LiteAIServer作为这一领域的佼佼者&#xff0c;凭借其卓越的技术实力与广泛的应用价值&#xff0c;正逐步成为守护公共安全的新利器…

跨境支付,哪些国产数据库能接得住?

最近有一个非常重大的事件&#xff0c;那就是10月22日-24日的金砖国家会议。金砖国家领导人第十六次会晤是金砖国家进一步凝聚共识、以实际行动推动“全球南方”共同发展进步的重要机遇。 酝酿已久的金砖跨境支付体系&#xff0c;也在这次峰会中正式推出。金砖国家的支付系统一…

CSS行块标签的显示方式

块级元素 标签&#xff1a;h1-h6&#xff0c;p,div,ul,ol,li,dd,dt 特点&#xff1a; &#xff08;1&#xff09;如果块级元素不设置默认宽度&#xff0c;那么该元素的宽度等于其父元素的宽度。 &#xff08;2&#xff09;所有的块级元素独占一行显示. &#xff08;3&#xff…

动态规划 —— 斐波那契数列模型-最小花费爬楼梯

1. 最小花费爬楼梯 题目链接&#xff1a; 746. 使用最小花费爬楼梯 - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/min-cost-climbing-stairs/description/ 2. 题目解析 3. 算法原理 1. 状态表示&#xff1a;以i位置为结尾 dp[i]表示&#xff1a;到…

《关于构图问题》

这是一本讲绘画技巧的书&#xff0c;但仔细琢磨体现出不易察觉的东方哲学思想。中国画讲究意境与留白&#xff0c;留白不代表“空”&#xff0c;而是代表对“实”的延伸&#xff0c;留下瞎想空间&#xff0c;实现对“有限&#xff08;实&#xff09;”的超越。 总论 文艺是人们…