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.rhkb.cn/news/457926.html

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

相关文章

web服务实验

http实验 先创建需要访问的web页面文件index.html 编辑vim /etc/nginx/conf.d/testip.conf 测试通过域名访问需要编辑/etc/hosts 如果通过windows的浏览器访问需要编辑下面的文件通过一管理员身份打开的记事本编辑 C:\Windows\System32\drivers\etc下的hosts文件 192.168.1…

软考:GPU算力,AI芯片

算力 FLOPS&#xff08;每秒浮点操作&#xff09; NVIDIA 去年就有超过 1 exa 的新闻&#xff0c;所以这个数值是越大越好。 AI芯片的技术架构类型 GPU&#xff1a;图形处理单元&#xff0c;擅长并行处理&#xff0c;适用于深度学习等AI计算密集型任务。FPGA&#xff1a;现…

OpenStack将运行的系统导出 QCOW2 镜像并导入阿里云

项目背景 OpenStack&#xff0c;作为一个开源的云计算平台&#xff0c;经常被用于构建私有云和公有云服务。然而&#xff0c;随着业务的发展和扩展&#xff0c;企业可能会面临将在OpenStack上运行的虚拟机迁移到其他云服务供应商的需求 需求 因为运营团队在本地机房有一台 O…

Netty-TCP服务端粘包、拆包问题(两种格式)

前言 最近公司搞了个小业务&#xff0c;需要使用TCP协议&#xff0c;我这边负责服务端。客户端是某个设备&#xff0c;客户端传参格式、包头包尾等都是固定的&#xff0c;不可改变&#xff0c;而且还有个蓝牙传感器&#xff0c;透传数据到这个设备&#xff0c;然后通过这个设备…

pandas快速入门

pandas快速入门 1. 创建pandas对象1.1 前言1.2 使用DataFrame类创建pandas对象1.3 对DataFrame对象进行索引1.4 使用Series类创建pandas对象1.5 对DataFrame Series对象使用常见方法 2. pandas读取文件2.1 使用pd.read_*方法读取文件2.2 使用to_*保存数据2.3 使用info()方法查看…

Python 判断键是否存在字典中(新手入门、实战案例)

在早期的Python2版本中&#xff0c;可以使用 dict.has_key()方法来判断一个键是否存在于字典中。 在Python3中&#xff0c;dict.has_key()方法被废弃了&#xff0c;不能再被使用。如果在Python3中尝试使用dict.has_key()方法会导致 AttributeError异常。 那在Python3中要如何判…

Linux:指令再认识

文章目录 前言一、知识点1. Linux下一切皆文件&#xff0c;也就是说显示器也是一种文件2. 指令是什么&#xff1f;3. ll 与 ls -l4. 日志5. 管道6. 时间戳 二、基本指令1. man指令2. cp指令3. mv指令4. 查看文件1&#xff09;cat/tac指令——看小文件2&#xff09;more/less指令…

Qt:QtCreator使用

用一个QtCreator适配所有Qt版本 首先Qt和QtCreator版本并不是通用的&#xff0c;一个电脑中可以安装很多个Qt版本&#xff0c;但只需要安装一个最新版本的QtCreator即可 Qt是一个语言&#xff0c;也可理解为一个SDK库&#xff0c;Qt目前最新版本为6.7 QtCreator是一个集成开发…

嵌入式Linux的AXI平台(platform)驱动教程

本文以JFMQL100的Linux系统的AXI接口的平台驱动为例&#xff0c;介绍嵌入式Linux的平台驱动编写、测试软件编写以及验证方式。本文的方法适用于任意嵌入式芯片Linux的物理地址映射的平台&#xff08;platform&#xff09;驱动的编写、测试与应用。 本文中AXI的开始地址为0x8000…

Python浪漫之画星星

效果图&#xff08;动态的哦&#xff01;&#xff09;&#xff1a; 完整代码&#xff08;上教程&#xff09;&#xff1a; import turtle import random import time # 导入time模块# 创建一个画布 screen turtle.Screen() screen.bgcolor("red")# 创建一个海龟&a…

Coppelia Sim (v-REP)仿真 机器人3D相机手眼标定与实时视觉追踪 (一)

coppelia sim[V-REP]仿真实现 机器人于3D相机手眼标定与实时视觉追踪 一 标定板的制作生成标定的PDF文件PDF转为图像格式图像加载到仿真中 二 仿真场景设置加载机器人加载的控制dummy ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/b48549d355d8441d8dfc20bc7ba7196…

MYSQL作业三

准备工作 查询操作

Linux复习-C++

参考博客&#xff1a; https://blog.csdn.net/qq_45254369/article/details/126023482?ops_request_misc%257B%2522request%255Fid%2522%253A%252277629891-A0F3-4EFC-B1AC-410093596085%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&req…

力扣 167. 两数之和 II - 输入有序数组

目录 一、题目二、思路三、代码 一、题目 二、思路 由于数组是 非递减顺序排列 的&#xff0c;利用该先验知识&#xff0c;可以设置两个指针在数组的起始和末尾&#xff0c;相向前进&#xff0c;与暴力枚举相比可以加快寻找速度。 三、代码 class Solution {public int[] tw…

面向对象(上)

1.初始对象 学习目标&#xff1a;理解使用对象完成数据组织的思路 可以看到加入了登记表后&#xff0c;我们学生所提供的信息就组织的简洁明了 程序中也是有多种多样的方式来记录数据 思考&#xff1a;使用变量记录数据太乱了&#xff0c;如果程序中也和生活中一样&#xff…

Linux - 动静态库

文章目录 一、静态库1、定义2、文件扩展名3、生成静态库4、使用静态库5、静态库的优缺点 二、动态库1、定义2、文件扩展名3、生成动态库4、使用动态库5、动态库的优缺点6、简单动态库加载 三、补充 一、静态库 1、定义 静态库是在编译时链接到最终的可执行文件中的库。这意味着…

Java Web项目实战:从零基础到项目开发全流程

Java Web开发环境搭建 Java Web开发需要Java运行环境、IDE&#xff08;如IntelliJ IDEA或Eclipse&#xff09;以及Maven集成开发工具等。首先&#xff0c;确保电脑上安装有Java 8或更高版本&#xff0c;可以通过访问Java官网获取最新版本。其次&#xff0c;安装IDE&#xff0c…

深入理解跳出率:如何利用百度统计优化网站用户体验

在数字营销的世界中&#xff0c;跳出率是一个关键指标&#xff0c;它衡量了用户对网站的第一印象和内容的吸引力。最近&#xff0c;我在分析我的网站数据时发现&#xff0c;跳出率常常高达100%&#xff0c;这促使我深入研究了跳出率的含义和影响因素。 跳出率的定义与计算 跳…

AndroidStudio Koala更改jdk版本 2024-1-2

升级android studio版本到 AndroidStudio Koala之后&#xff0c;发现修改jdk版本的方式不同了。如下&#xff1a; 修改jdk版本

《Python网络安全项目实战》

《Python网络安全项目实战》 项目1 Python 环境安装任务1.1 Windows上安装Python任务1.2 Ubuntu环境下安装Python 项目2 Python基础练习任务2.1 使用数据类型任务2.2 使用组合数据类型任务2.3 使用控制结构任务2.4 使用函数任务2.5 使用模块 项目3 处理文件中的数据任务3.1 读文…