compareAndSet----比較并交換
AtomicInteger.conpareAndSet(int expect, indt update)
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
第一個(gè)參數(shù)為拿到的期望值厚宰,如果期望值沒(méi)有一致涤久,進(jìn)行update賦值,如果期望值不一致袁勺,證明數(shù)據(jù)被修改過(guò),返回fasle咒钟,取消賦值
例子:
package com.jian8.juc.cas;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 1.CAS是什么疏哗?
* 1.1比較并交換
*/
public class CASDemo {
public static void main(String[] args) {
checkCAS();
}
public static void checkCAS(){
AtomicInteger atomicInteger = new AtomicInteger(5);
System.out.println(atomicInteger.compareAndSet(5, 2019) + "\t current data is " + atomicInteger.get());
System.out.println(atomicInteger.compareAndSet(5, 2014) + "\t current data is " + atomicInteger.get());
}
}
輸出結(jié)果為:
true current data is 2019
false current data is 2019
2、CAS底層原理庐镐?對(duì)Unsafe的理解
比較當(dāng)前工作內(nèi)存中的值和主內(nèi)存中的值蒋伦,如果相同則執(zhí)行規(guī)定操作,否則繼續(xù)比較知道主內(nèi)存和工作內(nèi)存中的值一直為止
-
atomicInteger.getAndIncrement();
public final int getAndIncrement() { return unsafe.getAndAddInt(this, valueOffset, 1); }
-
Unsafe
-
是CAS核心類(lèi)焚鹊,由于Java方法無(wú)法直接訪(fǎng)問(wèn)地層系統(tǒng)痕届,需要通過(guò)本地(native)方法來(lái)訪(fǎng)問(wèn),Unsafe相當(dāng)于一個(gè)后門(mén)末患,基于該類(lèi)可以直接操作特定內(nèi)存數(shù)據(jù)研叫。Unsafe類(lèi)存在于
sun.misc
包中,其內(nèi)部方法操作可以像C的指針一樣直接操作內(nèi)存璧针,因?yàn)镴ava中CAS操作的執(zhí)行依賴(lài)于Unsafe類(lèi)的方法嚷炉。Unsafe類(lèi)中的所有方法都是native修飾的,也就是說(shuō)Unsafe類(lèi)中的方法都直接調(diào)用操作系統(tǒng)底層資源執(zhí)行相應(yīng)任務(wù)
變量valueOffset探橱,表示該變量值在內(nèi)存中的偏移地址申屹,因?yàn)閁nsafe就是根據(jù)內(nèi)存便宜地址獲取數(shù)據(jù)的
變量value用volatile修飾,保證多線(xiàn)程之間的可見(jiàn)性
-
-
CAS是什么
CAS全稱(chēng)呼Compare-And-Swap隧膏,它是一條CPU并發(fā)原語(yǔ)
他的功能是判斷內(nèi)存某個(gè)位置的值是否為預(yù)期值哗讥,如果是則更改為新的值,這個(gè)過(guò)程是原子的胞枕。
CAS并發(fā)原語(yǔ)體現(xiàn)在JAVA語(yǔ)言中就是sun.misc.Unsafe類(lèi)中各個(gè)方法杆煞。調(diào)用Unsafe類(lèi)中的CAS方法,JVM會(huì)幫我們實(shí)現(xiàn)CAS匯編指令腐泻。這是一種完全依賴(lài)于硬件的功能决乎,通過(guò)他實(shí)現(xiàn)了原子操作。由于CAS是一種系統(tǒng)原語(yǔ)派桩,原語(yǔ)屬于操作系統(tǒng)用語(yǔ)范疇构诚,是由若干條指令組成的,用于完成某個(gè)功能的一個(gè)過(guò)程铆惑,并且原語(yǔ)的執(zhí)行必須是連續(xù)的范嘱,在執(zhí)行過(guò)程中不允許被中斷送膳,也就是說(shuō)CAS是一條CPU的原子指令,不會(huì)造成數(shù)據(jù)不一致問(wèn)題彤侍。
//unsafe.getAndAddInt 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 AtomicInteger對(duì)象本身
var2 該對(duì)象的引用地址
var4 需要變動(dòng)的數(shù)據(jù)
var5 通過(guò)var1 var2找出的主內(nèi)存中真實(shí)的值
用該對(duì)象前的值與var5比較肠缨;
如果相同,更新var5+var4并且返回true盏阶,
如果不同晒奕,繼續(xù)去之然后再比較,直到更新完成
3名斟、CAS缺點(diǎn)
-
** 循環(huán)時(shí)間長(zhǎng)脑慧,開(kāi)銷(xiāo)大**
例如getAndAddInt方法執(zhí)行,有個(gè)do while循環(huán)砰盐,如果CAS失敗闷袒,一直會(huì)進(jìn)行嘗試,如果CAS長(zhǎng)時(shí)間不成功岩梳,可能會(huì)給CPU帶來(lái)很大的開(kāi)銷(xiāo)
-
只能保證一個(gè)共享變量的原子操作
對(duì)多個(gè)共享變量操作時(shí)囊骤,循環(huán)CAS就無(wú)法保證操作的原子性,這個(gè)時(shí)候就可以用鎖來(lái)保證原子性
ABA問(wèn)題
三冀值、原子類(lèi)AtomicInteger的ABA問(wèn)題也物?原子更新引用?
1列疗、ABA如何產(chǎn)生
CAS算法實(shí)現(xiàn)一個(gè)重要前提需要去除內(nèi)存中某個(gè)時(shí)刻的數(shù)據(jù)并在當(dāng)下時(shí)刻比較并替換滑蚯,那么在這個(gè)時(shí)間差類(lèi)會(huì)導(dǎo)致數(shù)據(jù)的變化。
比如線(xiàn)程1從內(nèi)存位置V取出A抵栈,線(xiàn)程2同時(shí)也從內(nèi)存取出A告材,并且線(xiàn)程2進(jìn)行一些操作將值改為B,然后線(xiàn)程2又將V位置數(shù)據(jù)改成A古劲,這時(shí)候線(xiàn)程1進(jìn)行CAS操作發(fā)現(xiàn)內(nèi)存中的值依然時(shí)A斥赋,然后線(xiàn)程1操作成功。
==盡管線(xiàn)程1的CAS操作成功绢慢,但是不代表這個(gè)過(guò)程沒(méi)有問(wèn)題==
2灿渴、如何解決?原子引用
示例代碼:
package juc.cas;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.ToString;
import java.util.concurrent.atomic.AtomicReference;
public class AtomicRefrenceDemo {
public static void main(String[] args) {
User z3 = new User("張三", 22);
User l4 = new User("李四", 23);
AtomicReference<User> atomicReference = new AtomicReference<>();
atomicReference.set(z3);
System.out.println(atomicReference.compareAndSet(z3, l4) + "\t" + atomicReference.get().toString());
System.out.println(atomicReference.compareAndSet(z3, l4) + "\t" + atomicReference.get().toString());
}
}
@Getter
@ToString
@AllArgsConstructor
class User {
String userName;
int age;
}
輸出結(jié)果
true User(userName=李四, age=23)
false User(userName=李四, age=23)
3胰舆、時(shí)間戳的原子引用
新增機(jī)制,修改版本號(hào)
package com.jian8.juc.cas;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicStampedReference;
/**
* ABA問(wèn)題解決
* AtomicStampedReference
*/
public class ABADemo {
static AtomicReference<Integer> atomicReference = new AtomicReference<>(100);
static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100, 1);
public static void main(String[] args) {
System.out.println("=====以下時(shí)ABA問(wèn)題的產(chǎn)生=====");
new Thread(() -> {
atomicReference.compareAndSet(100, 101);
atomicReference.compareAndSet(101, 100);
}, "Thread 1").start();
new Thread(() -> {
try {
//保證線(xiàn)程1完成一次ABA操作
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(atomicReference.compareAndSet(100, 2019) + "\t" + atomicReference.get());
}, "Thread 2").start();
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("=====以下時(shí)ABA問(wèn)題的解決=====");
new Thread(() -> {
int stamp = atomicStampedReference.getStamp();
System.out.println(Thread.currentThread().getName() + "\t第1次版本號(hào)" + stamp);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
atomicStampedReference.compareAndSet(100, 101, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);
System.out.println(Thread.currentThread().getName() + "\t第2次版本號(hào)" + atomicStampedReference.getStamp());
atomicStampedReference.compareAndSet(101, 100, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);
System.out.println(Thread.currentThread().getName() + "\t第3次版本號(hào)" + atomicStampedReference.getStamp());
}, "Thread 3").start();
new Thread(() -> {
int stamp = atomicStampedReference.getStamp();
System.out.println(Thread.currentThread().getName() + "\t第1次版本號(hào)" + stamp);
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
boolean result = atomicStampedReference.compareAndSet(100, 2019, stamp, stamp + 1);
System.out.println(Thread.currentThread().getName() + "\t修改是否成功" + result + "\t當(dāng)前最新實(shí)際版本號(hào):" + atomicStampedReference.getStamp());
System.out.println(Thread.currentThread().getName() + "\t當(dāng)前最新實(shí)際值:" + atomicStampedReference.getReference());
}, "Thread 4").start();
}
}
輸出結(jié)果:
=====以下時(shí)ABA問(wèn)題的產(chǎn)生=====
true 2019
=====以下時(shí)ABA問(wèn)題的解決=====
Thread 3 第1次版本號(hào)1
Thread 4 第1次版本號(hào)1
Thread 3 第2次版本號(hào)2
Thread 3 第3次版本號(hào)3
Thread 4 修改是否成功false 當(dāng)前最新實(shí)際版本號(hào):3
Thread 4 當(dāng)前最新實(shí)際值:100