成人在线亚洲_国产日韩视频一区二区三区_久久久国产精品_99国内精品久久久久久久

您的位置:首頁(yè)技術(shù)文章
文章詳情頁(yè)

Java CAS基本實(shí)現(xiàn)原理代碼實(shí)例解析

瀏覽:72日期:2022-08-27 17:04:49

一、前言

了解CAS,首先要清楚JUC,那么什么是JUC呢?JUC就是java.util.concurrent包的簡(jiǎn)稱(chēng)。它有核心就是CAS與AQS。CAS是java.util.concurrent.atomic包的基礎(chǔ),如AtomicInteger、AtomicBoolean、AtomicLong等等類(lèi)都是基于CAS。

什么是CAS呢?全稱(chēng)Compare And Swap,比較并交換。CAS有三個(gè)操作數(shù),內(nèi)存值V,舊的預(yù)期值E,要修改的新值N。當(dāng)且僅當(dāng)預(yù)期值E和內(nèi)存值V相同時(shí),將內(nèi)存值V修改為N,否則什么都不做。

Java CAS基本實(shí)現(xiàn)原理代碼實(shí)例解析

二、實(shí)例

如果我們需要對(duì)一個(gè)數(shù)進(jìn)行加法操作,應(yīng)該怎樣去實(shí)現(xiàn)呢?我們模擬多個(gè)線(xiàn)程情況下進(jìn)行操作。

ThreadDemo.java 實(shí)現(xiàn)一個(gè)Runnable接口

package com.spring.security.test;public class ThreadDemo implements Runnable {private int count = 0;@Overridepublic void run() {for (int i = 0; i < 100; i++) {addCount();}}private void addCount() {count++;}public int getCount() {return count;}}

ThreadTest.java 創(chuàng)建線(xiàn)程池,提交10個(gè)線(xiàn)程執(zhí)行,預(yù)期結(jié)果應(yīng)該是1000

package com.spring.security.test;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class ThreadTest {public static void main(String[] args) {ExecutorService threadPool = Executors.newFixedThreadPool(10);ThreadDemo threadDemo = new ThreadDemo();for (int i = 0; i < 10; i++) {threadPool.submit(threadDemo);} threadPool.shutdown();System.out.println(threadDemo.getCount());}}

運(yùn)行結(jié)果:874 或其他,與預(yù)期結(jié)果不符合。

執(zhí)行出來(lái)的結(jié)果并不是想象中的結(jié)果。這是為什么呢?這跟線(xiàn)程的執(zhí)行過(guò)程有關(guān)。

Java CAS基本實(shí)現(xiàn)原理代碼實(shí)例解析

所以我們需要在改變count,將值從高速緩沖區(qū)刷新到主內(nèi)存后,讓其他線(xiàn)程重新讀取主內(nèi)存中的值到自己的工作內(nèi)存。

此時(shí)可以用volatile關(guān)鍵字。它的作用是保證對(duì)象在內(nèi)存中的可見(jiàn)性。

修改ThreadDemo中的count字段

private volatile int count = 0;

此時(shí)執(zhí)行結(jié)果:900 或其他,與預(yù)期結(jié)果不符合。

此時(shí)還是并未得出正確執(zhí)行結(jié)果。為什么?聽(tīng)我細(xì)細(xì)道來(lái)。

線(xiàn)程安全主要體現(xiàn)在三個(gè)方面:

原子性:提供了互斥訪(fǎng)問(wèn),同一時(shí)刻只能有一個(gè)線(xiàn)程對(duì)它進(jìn)行操作 可見(jiàn)性:一個(gè)線(xiàn)程對(duì)主內(nèi)存的修改可以及時(shí)的被其他線(xiàn)程觀(guān)察到 有序性:一個(gè)線(xiàn)程觀(guān)察其他線(xiàn)程中的指令執(zhí)行順序,由于指令重排序的存在,該觀(guān)察結(jié)果一般雜亂無(wú)序

目前可見(jiàn)性已經(jīng)實(shí)現(xiàn)了,缺少原子性的操作,因?yàn)橥粫r(shí)刻,多個(gè)線(xiàn)程對(duì)其操作,會(huì)將改動(dòng)后的最新值讀取到自己的工作內(nèi)存進(jìn)行操作,最終只能得到后一個(gè)執(zhí)行線(xiàn)程操作的結(jié)果,所以相當(dāng)于少了一步操作,就會(huì)造成數(shù)據(jù)的不一致。

此時(shí)可以使用JUC的Atomic包下面的類(lèi)來(lái)進(jìn)行操作。

Java CAS基本實(shí)現(xiàn)原理代碼實(shí)例解析

Atomic類(lèi)是使用CAS+volatile來(lái)實(shí)現(xiàn)原子性與可見(jiàn)性的。

我們來(lái)改造一下TheadDemo.java中的實(shí)現(xiàn)方法

package com.spring.security.test;import java.util.concurrent.atomic.AtomicInteger;public class ThreadDemo implements Runnable {private AtomicInteger count = new AtomicInteger(0);@Overridepublic void run() {for (int i = 0; i < 100; i++) {// 遞增count.getAndIncrement();}}public int getCount() {return count.get();}}

執(zhí)行結(jié)果: 1000,符合預(yù)期值。

Java CAS基本實(shí)現(xiàn)原理代碼實(shí)例解析

接下來(lái)我們來(lái)分析一下AtomicInteger類(lèi)的源碼:

private static final Unsafe unsafe = Unsafe.getUnsafe();private static final long valueOffset;static { try { valueOffset = unsafe.objectFieldOffset (AtomicInteger.class.getDeclaredField('value')); } catch (Exception ex) { throw new Error(ex); }}private volatile int value;

Unsafe類(lèi)是不安全的類(lèi),它提供了一些底層的方法,我們是不能使用這個(gè)類(lèi)的。AtomicInteger的值保存在value中,而valueOffset是value在內(nèi)存中的偏移量,利用靜態(tài)代碼塊使其類(lèi)一加載的時(shí)候就賦值。value值使用volatile,保證其可見(jiàn)性。

/** * Atomically increments by one the current value. * * @return the previous value */ public final int getAndIncrement() { return unsafe.getAndAddInt(this, valueOffset, 1); }

public final int getAndAddInt(Object var1, long var2, int var4) {int var5;do {var5 = this.getIntVolatile(var1, var2);} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));return var5;}

var1表示當(dāng)前對(duì)象,var2表示value在內(nèi)存中的偏移量,var4為增加的值。var5為調(diào)用底層方法獲取value的值

compareAndSwapInt方法通過(guò)var1和var2獲取當(dāng)前內(nèi)存中的value值,并與var5進(jìn)行比對(duì),如果一致,就將var5+var4的值賦給value,并返回true,否則返回false

由do while語(yǔ)句可知,如果這次沒(méi)有設(shè)置進(jìn)去值,就重復(fù)執(zhí)行此過(guò)程。這一過(guò)程稱(chēng)為自旋。

compareAndSwapInt是JNI(Java Native Interface)提供的方法,可以是其他語(yǔ)言寫(xiě)的。

三、與synchronized比較

使用synchronized進(jìn)行加法:

package com.spring.security.test;public class ThreadDemo implements Runnable {private int count = 0;@Overridepublic void run() {for (int i = 0; i < 100; i++) {// 遞增synchronized (ThreadDemo.class) {count++;}}}public int getCount() {return count;}}

運(yùn)行結(jié)果: 1000,符合預(yù)期值。

444

使用synchronized和AtomicInteger都能得到預(yù)期結(jié)果,但是他們之間各有什么劣勢(shì)呢?

synchronized是重量級(jí)鎖,是悲觀(guān)鎖,就是無(wú)論你線(xiàn)程之間發(fā)不發(fā)生競(jìng)爭(zhēng)關(guān)系,它都認(rèn)為會(huì)發(fā)生競(jìng)爭(zhēng),從而每次執(zhí)行都會(huì)加鎖。

在并發(fā)量大的情況下,如果鎖的時(shí)間較長(zhǎng),那將會(huì)嚴(yán)重影響系統(tǒng)性能。

CAS操作中我們可以看到getAndAddInt方法的自旋操作,如果長(zhǎng)時(shí)間自旋,那么肯定會(huì)對(duì)系統(tǒng)造成壓力。而且如果value值從A->B->A,那么CAS就會(huì)認(rèn)為這個(gè)值沒(méi)有被操作過(guò),這個(gè)稱(chēng)為CAS操作的'ABA'問(wèn)題。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持好吧啦網(wǎng)。

標(biāo)簽: Java
相關(guān)文章: