日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Java线程CAS原子操作

發布時間:2025/3/11 java 15 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java线程CAS原子操作 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

這次分享一些關于原子操作(CAS)的東西.

定義

CAS(Compare And Swap)是CPU的一個指令級別的操作,叫原子操作,原子操作是不可分割的,跟事務差不多,要么全部執行完成,要么不執行;

像這種操作有點類似阻塞鎖機制,但是使用阻塞鎖機制去控制的話,會比較消耗性能,而使用CAS操作的話會比使用鎖更快。

synchronized性能比較,最簡單的一個測試Demo

public class AtomicNumber {AtomicInteger ai = new AtomicInteger(0);int i = 0;public synchronized void addInteger() {i ++ ;}public void addAtomicInteger() {ai.getAndIncrement();}public static void main(String[] args) throws InterruptedException {long time = System.currentTimeMillis();final AtomicNumber atomicNumber = new AtomicNumber();final CountDownLatch latch = new CountDownLatch(2);int count = 0;while(count<2) {new Thread(new Runnable() {public void run() {for(int j=0;j<200000;j++)atomicNumber.addAtomicInteger();latch.countDown();}}).start();count++;}latch.await();System.out.println("花費的時間:"+(System.currentTimeMillis()-time) + "ms");System.out.println("atomicInteger value:"+atomicNumber.ai.get());final CountDownLatch latch1 = new CountDownLatch(2);count = 0;time = System.currentTimeMillis();while(count<2) {new Thread(new Runnable() {public void run() {for(int j=0;j<200000;j++)atomicNumber.addInteger();latch1.countDown();}}).start();count++;}latch1.await();System.out.println("花費的時間:"+(System.currentTimeMillis()-time) + "ms");System.out.println("i value:"+atomicNumber.i);} }//運行的結果: 花費的時間:14ms atomicInteger value:400000 花費的時間:35ms i value:400000 //很明顯,使用原子操作會比使用鎖機制要快。

CAS里面有三個操作數:1、內存地址(V);2、期望的值(A);3、新值(B)

主要思想:如果內存地址V的期望值等于A時,則將地址V賦值給新值B,如果不相等,算CAS操作失敗則不做任何操作;但觸發了CAS操作,如果內存地址上的值V不等于A的話,就會進入死循環,一直做CAS操作,一直到相等也就是成功,這個循環過程叫自旋

存在的問題

1、ABA問題:即當取出內存地址V的時候,期望值從A變成新值B,然后有從B變成A值,然后再將V與期望值相比發現一值,其實這個過程確實發生了變化,只是結果值與初始值一致;

像這樣的問題,我們可以采用兩個方式,第一種就是在期望值變化的時候加上一個版本號(AtomicStampedReference)從A1變成B2變成A3這樣就能解決這樣的問題了,另外一種就是變化了就標記期望值已變化(AtomicMarkableReference)。

2、開銷比較大:長期處于自旋的CAS操作,會導致性能消耗。

3、只能保證只有一個共享變量進行原子操作,在Java中有個專門處理的類來解決這個問題(AtomicReference)

JDK提供的原子操作類

AtomicInteger

用法在上面的那個案例有了就不多說了,有幾個需要注意的地方:

1、在使用自增的方法有兩個:1)、getAndIncrement()?這個是先獲取值,在自增。2)、incrementAndGet()?先自增然后在獲取值。這兩個方法的差別有點類似?i++?以及 ++i的操作。

AtomicReference

這個是原子操作引用類型,結果來看,原子引用并不會改變原始的值。

static AtomicReference<UserInfo> reference = new AtomicReference<UserInfo>();public static void main(String[] args) {UserInfo userInfo1 = new UserInfo("Mark",12);reference.set(userInfo1);UserInfo userInfo2 = new UserInfo("Mark12",23);reference.compareAndSet(userInfo1, userInfo2);System.out.println("new data:");System.out.println(reference.get().getName());System.out.println(reference.get().getAge());System.out.println("old reference object:");System.out.println(userInfo1.getName());System.out.println(userInfo1.getAge());}static class UserInfo {String name;Integer age;public UserInfo(String name, Integer age) {super();this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}}//運行結果: new data: Mark12 23 old reference object: Mark 12

AtomicStampedReference

在期望值變化的時候加上一個版本號,在做CAS操作的時候,如果不一致會返回為false,并且不會做任何操作

/*** <p>需要一個初始值以及初始版本號*/static AtomicStampedReference<String> reference = new AtomicStampedReference<String>("Java",0);public static void main(String[] args) throws InterruptedException {final String value = reference.getReference();final int stamp = reference.getStamp();System.out.println("value:" + value + "====== version:"+stamp);Thread correctThread = new Thread(new Runnable() {public void run() {System.out.println(Thread.currentThread().getName() + " old reference:"+value + " version:"+stamp + " CAS STATUS : " + reference.compareAndSet(value, value + " is the beast Language", stamp, 1));System.out.println(Thread.currentThread().getName() + "value:" + reference.getReference() + "====== version:"+reference.getStamp());}});Thread errorThread = new Thread(new Runnable() {public void run() {System.out.println(Thread.currentThread().getName() + " old reference:"+reference.getReference() + " version:"+reference.getStamp() + " CAS STATUS : " + reference.compareAndSet(value, value + " Hello World!!", stamp, 2));System.out.println(Thread.currentThread().getName() + "value:" + reference.getReference() + "====== version:"+reference.getStamp());}});correctThread.start();correctThread.join();errorThread.start();errorThread.join();System.out.println("value:" + reference.getReference() + "====== version:"+reference.getStamp());}//運行結果: value:Java====== version:0 value:Java====== version:0 Thread-0 old reference:Java version:0 CAS STATUS : true Thread-1 old reference:Java version:0 CAS STATUS : false Thread-0value:Java is the beast Language====== version:1 Thread-1value:Java is the beast Language====== version:1

目前就這么多啦,至于AtomicMarkableReference這個原子引用大家了自己去試一下跟AtomicStampedReference差不多,好啦該洗洗睡了。

總結

以上是生活随笔為你收集整理的Java线程CAS原子操作的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。