一文了解JAVA CAS
admin
2024-05-23 02:51:16
0次

前言

首先我们看下面这一段代码,具体实现的功能是创建100个线程,每个线程对初始化为0的整数进行1万次+1操作,等待这100个线程跑完后输出这个整数的值,理论上这个整数最后输出的数值为100万。

import java.util.concurrent.CountDownLatch;public class AddTest {private volatile long i = 0;private void add() {i++;} private void println() {System.out.println(i);}private static final int THREAD_NUM = 100;public static void main(String str[]) throws Exception {CountDownLatch latch = new CountDownLatch(THREAD_NUM);AddTest addTest = new AddTest();for (int i = 0; i < THREAD_NUM; ++i) {new Thread(() -> {for (int j = 0; j != 10000; ++j) {addTest.add();}latch.countDown();}).start();}//等待100个线程跑完latch.await();addTest.println();}
}

实际上跑完后的结果为400131,显然和预期的100万不符合,多次跑还会结果不一样,但是结果都少于100万,为什么呢?

400131进程已结束,退出代码0

问题原因

因为i++并不是一个原子操作,更新整数i其实包含一下3个步骤:

  1. 读取当前值。
  2. 执行递增操作以获取更新的值。
  3. 将更新的值分配给字段引用。

这些都作为不同的任务执行,因此,当一个线程正在读取值时,另一个线程可能正在递增它,而另一个线程可能正在将更新的值分配给字段引用。为了加深理解,我们引入最简单的C语言i++的代码,如下。

int main()
{int i = 0;i++;return 0;
}

利用gcc工具将C语言代码转为汇编语言。

gcc -S test.c -o test.s

得到test.s文件,其中i++的汇编执行指令如下,更新i的步骤有3个,首先读取当前值,其次执行必要的操作以获取更新的值,再将更新的值分配给字段引用。

movl	-8(%rbp), %ecx
addl	$1, %ecx
movl	%ecx, -8(%rbp)

以上汇编是AT&T汇编语法,平时学习常见的是Intel汇编语法(mov,add等指令),具体这2个语法差异可以跳到这个链接看看,这里不展开介绍。https://sdasgup3.github.io/Intel_Vs_Att_format/

在之前的java代码中,我们预期为100万的结果执行流程应该如下,每个线程中的movl,addl,movl指令都是作为整体一起执行的,该线程执行完毕之后才到下一个线程执行。

指令
thread1-1movl -8(%rbp), %ecx
thread1-2addl $1, %ecx
thread1-3movl %ecx, -8(%rbp)
thread2-4movl -8(%rbp), %ecx
thread2-5addl $1, %ecx
thread2-6movl %ecx, -8(%rbp)

但是实际上执行的执行流程有可能如下面的表格一样。

thread1thread2
1movl -8(%rbp), %ecx
2movl -8(%rbp), %ecx
3addl $1, %ecx
4movl %ecx, -8(%rbp)
5addl $1, %ecx
6movl %ecx, -8(%rbp)

线程1先把当前的值(i=0)存到寄存器%ecx,然后轮到线程2把当前的值存到寄存器%ecx,这个时候2个线程获取到的当前值都是一样的,接着线程2把i+1的结果存到寄存器%ecx,再把寄存器%ecx的值取出来更新到i为1。然后轮到线程1接着进行addl和movl操作,由于线程1和2之前获取的当前值(i)一样,所以最终线程1也把i更新1。

解决方案之一CAS

为了解决以上并发问题,在JDK1.5之后,提供了CAS(全称Compare And Swap)操作,该操作由sun.misc.Unsafe类里面的compareAndSwapInt()和compareAndSwapLong()等几个方法包装提供。一句话概括该原理就是,根据内存地址获得期望值,更新的时候再次根据原来的内存地址获取值,如果和期望值一样,则进行更新,否则更新失败。

这里的内存地址是指对象地址+变量偏移值。

其中AtomicLong对象的实现就是基于CAS的,下面是核心的代码。

    /*** var1为对象地址,var2为变量偏移值*根据对象地址和变量偏移值就能获取到对象参数的值,该值为期望值var6。* */public final long getAndAddLong(Object var1, long var2, long var4) {long var6;do {//获取期望值,这里已经移交给外部程序执行了,是本地方法。var6 = this.getLongVolatile(var1, var2);//compareAndSwapLong也是本地方法,移交给外部的程序执行,看看更新那时候是不是还是var6,如果是就更新为var6 + var4,否则不更新。} while(!this.compareAndSwapLong(var1, var2, var6, var6 + var4));return var6;}

代码中的while循环,代表循环直到更新成功为止,因为每个线程执行一定有自己的执行任务,只是这个时刻不应该进行更新,如果缺少while循环会导致自增结果不准,因此在高并发下,会存在很多无效循环(划重点)。

所以我们可以使用AtomicLong对象来解决这个问题,更新后的代码如下。

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicLong;public class AddTest {public  AtomicLong i = new AtomicLong(0);private void add() {i.incrementAndGet();} private void println() {System.out.println(i.get());}private static final int THREAD_NUM = 100;public static void main(String str[]) throws Exception {CountDownLatch latch = new CountDownLatch(THREAD_NUM);AddTest addTest = new AddTest();for (int i = 0; i < THREAD_NUM; ++i) {new Thread(() -> {for (int j = 0; j != 10000; ++j) {addTest.add();}latch.countDown();}).start();}//等待100个线程跑完latch.await();addTest.println();}
}
1000000进程已结束,退出代码0

相关内容

热门资讯

蜻蜓订购APP贵金属订购非法期...   蜻蜓订购APP以"投资平台"名义对外宣传,主打贵金属期货(订购)交易。该平台一方面强调自身"正规...
悟空订购APP非法期货交易欺骗...   近期,有投资者反映,“悟空订购APP”等平台以白银、贵金属订购为名,宣传所谓“高收益”“低门槛”...
小赢淘金APP期货投资骗局,平...   “小赢淘金”之流,最擅长的就是用“注册送现金”、“充值返券”的糖衣炮弹来引流。别天真地以为那是福...
八方淘金APP非法期货交易欺骗...   警惕!八方生财、每日淘金、小赢淘金、悟空订购、蜻蜓订购、斗金订购等APP均涉嫌非法期货交易!  ...
智易购APP贵金属订购交易骗局...   很多人看到“3元起投”“充值送福利”“操作简单”“随时提现”这类宣传,很容易把智易购APP当成普...
喜玛银庄APP商品订购投资骗局...   喜玛银庄APP白银、铂金“现货订购”骗局警示:无资质平台涉嫌非法期货交易,投资者爆仓后如何维权?...
吉鑫乐享APP炒贵金属订购交易...   近期,多名投资者反映在“吉鑫乐享APP”进行贵金属投资时遭遇严重亏损,甚至血本无归。看似正规的“...
易掘金APP虚假期货投资骗局,...   “易掘金”的APP它打着“高收益、灵活投资”的旗号,宣称通过“商品订购”就能轻松赚钱,吸引了不少...
在大吉订购APP订购交易亏损被...   近期,不少投资者反映在“大吉订购App”上投资白银、铂金等商品遭遇严重亏损。该平台宣称提供现货订...
铂银壹号APP商品订购交易亏损...   许多中小投资者误以为网上的“商品现货订购”是合规合法的理财渠道,殊不知这背后隐藏着巨大的合规风险...
兴鑫淘金APP贵金属订购交易亏...   白银、贵金属或其他商品类投资,除国家批准的正规期货交易场所及持牌期货公司外,市面上大量第三方交易...
铂银掘金APP无资质的违规平台...   铂银掘金等平台的投资者,往往是被“注册送现金”“充值返现券”“首单免亏”等广告话术吸引,进而下载...
八方淘金APP订购交易不合规,...   “八方淘金APP”“铂银掘金APP”“铂银壹号APP”等软件被指以“现货订购”为名,实际采用高杠...
在指尖订购APP订购商品亏损被...   指尖订购APP这类平台,表面是“订购交易”,实则是披着电商外衣的非法期货陷阱。绝大多数投资者被蒙...
在犀贝易购APP做商品订购交易...   市面上打着“现货投资”“贵金属交易”旗号的APP并不少,但真正合规、受监管的平台并不多。犀贝易购...
易达淘金APP虚假商品订购交易...   易达淘金APP存在严重的虚假宣传与违规交易行为。该平台打着“商品订购”的旗号,实则通过不合规的交...
大浪淘金APP白银订购亏损被骗...   大浪淘金APP最隐蔽的套路,在于其完全封闭的内盘交易系统。表面上,它宣称与国际贵金属市场接轨;实...
一品好购APP上订购白银出现亏...   一品好购APP原名“一品订购”,运营方为上海宝智未来信息技术有限公司。近期不少投资者反映在该平台...
九州订购APP贵金属订购投资骗...   九州订购APP正通过抖.音、快手、今日头条等平台大量投放投资广告,用“5元起投”“充值返现”“0...
坚锅淘金APP交易软件并不正规...   当前,市面上大量所谓的“现货商城订购”交易软件,早已偏离了正常的实物商品买卖,演变为高杠杆、高手...