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差不多,好啦該洗洗睡了。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/537178.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/537178.shtml
英文地址,請注明出處:http://en.pswp.cn/news/537178.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

python 導航欄_解析導航欄的url--selnium,beautifulsoup實戰

前段時間做ui自動化測試的時候&#xff0c;導航欄菜單始終有點問題&#xff0c;最后只好直接獲取到url&#xff0c;然后直接使用driver.get(url)進入頁面&#xff1b;包括做壓測的時候&#xff0c;比如我要找出所有報表菜單的url&#xff0c;這樣不可能手動去一個一個找出來&am…

PNG圖片詳解

1、PNG圖片類型 PNG格式有8位、24位、32位三種&#xff0c;下面是一些術語&#xff1a; 索引透明&#xff1a;類似于GIF&#xff0c;某一像素只有全透和全不透明兩種效果Alpha透明&#xff1a;半透明PNG8 8位的PNG最多支持256&#xff08;2的8次方&#xff09;種顏色&#xff0…

Java并發編程之顯式鎖(Lock)使用

又是一個基于AQS好用的類&#xff0c;看來下次有必要看看AQS了&#xff0c;正好又是放假。 既然叫顯式鎖&#xff0c;必然也有隱式鎖&#xff0c;也就是所謂的synchronzied關鍵字&#xff0c;它們兩者的區別呢在于使用范圍&#xff0c;synchronzied關鍵字的使用范圍比Lock要小…

python pychart三維_詳解python模塊pychartdir安裝及導入問題

在遷移別人寫好的腳本時&#xff0c;發現pychartdir沒有導入&#xff0c;腳本執行報錯。以下是報錯內容&#xff1a;[modpsLGJF-ZYC5-MMSC-WEB02 ~]$ python /opt/aspire/product/modps/mopps/shell/dayreport_linux.py/etc/host.conf: line 1: bad command nospoof on"Tr…

vim 中Ctags的安裝和使用

Ctags是一個用來為源文件中的標識符&#xff08;如變量、函數、類成員、宏定義等&#xff09;創建索引文件的程序。這些tags文件能被編輯器或其它工具用來快速查找定位源代碼中的符號&#xff08;tag/symbol&#xff09;&#xff0c;如變量名&#xff0c;函數名等。 Tags文件中…

Java并發編程之AbstractQueuedSynchronizer(AQS)源碼解析

自己一個人隨便看看源碼學習的心得&#xff0c;分享一下啦&#xff0c;不過我覺得還是建議去買本Java并發編程的書來看會比較好點&#xff0c;畢竟個人的理解有限嘛。 獨占鎖和共享鎖 首先先引入這兩個鎖的概念&#xff1a;獨占鎖即同一時刻只有一個線程才能獲取到鎖&#xf…

采集用python還是火車頭_我才知道爬蟲也可以醬紫--火車采集器

我才知道爬蟲還可以這樣—火車采集器的使用說在前面額。。。好吧&#xff0c;我這一個三毛錢的屌絲也開始步入實習階段了&#xff0c;在北京其實也挺好的&#xff0c;雖說壓力大&#xff0c;但是今后就業機會也相對而言大一些。好了&#xff0c;說回今天的主題&#xff0c;之前…

mvn 使用中的錯誤

出現這種錯誤的時候&#xff1a;mvn Error building POM may not be this projects POM&#xff0c;報的是那個jar 包&#xff0c;就刪除那個jar 包&#xff0c;重新mvn clean install .ok

Java并發編程之FutureTask源碼解析

上次總結一下AQS的一些相關知識&#xff0c;這次總結了一下FutureTask的東西&#xff0c;相對于AQS來說簡單好多呀 之前提到過一個LockSupport的工具類&#xff0c;也了解一下這個工具類的用法&#xff0c;這里也鞏固一下吧 /*** Makes available the permit for the given th…

java 刪除二維數組中的null_避免在Java中檢查Null語句

1.概述通常&#xff0c;在Java代碼中處理null變量、引用和集合很棘手。它們不僅難以識別&#xff0c;而且處理起來也很復雜。事實上&#xff0c;在編譯時無法識別處理null的任何錯誤&#xff0c;會導致運行時NullPointerException。在本教程中&#xff0c;我們將了解在Java中檢…

Java并發編程之并發容器ConcurrentHashMap(JDK1.7)解析

最近看了一下ConcurrentHashMap的相關代碼&#xff0c;感覺JDK1.7和JDK1.8差別挺大的&#xff0c;這次先看下JDK1.7是怎么實現的吧 哈希&#xff08;hash&#xff09; 先了解一下啥是哈希&#xff08;網上有很多介紹&#xff09;&#xff0c;是一種散列函數&#xff0c;簡單來…

帶控制端的邏輯運算電路_分別完成正整數的平方、立方和階乘的運算verilog語言...

練習&#xff1a;設計一個帶控制端的邏輯運算電路&#xff0c;分別完成正整數的平方、立方和階乘的運算。 //--------------myfunction---------- modulemyfunction(clk,n,result,reset,sl); output[6:0]result; input[2:0] n; input reset,clk; input [1:0] sl; reg[6:0]resul…

Java并發編程之并發容器ConcurrentHashMap(JDK1.8)解析

這個版本ConcurrentHashMap難度提升了很多&#xff0c;就簡單的談一下常用的方法就好了&#xff0c;可能有些講的不太清楚&#xff0c;麻煩發現的大佬指正一下 主要數據結構 1.8將Segment取消了&#xff0c;保留了table數組的形式&#xff0c;但是不在以HashEntry純鏈表的形式…

simulink顯示多個數據_如何在 Simulink 中使用 PID Tuner 進行 PID 調參?

作者 | 安布奇責編 | 胡雪蕊出品 | CSDN(ID: CSDNnews)本文為一篇技術干貨&#xff0c;主要講述在Simulink如何使用PID Tuner進行PID調參。PID調參器( PIDTuner)概述1.1 簡介使用PID Tuner可以對Simulink模型中的PID控制器&#xff0c;離散PID控制器&#xff0c;兩自由度PID控制…

Java并發編程之堵塞隊列介紹以及SkipList(跳表)

堵塞隊列 先了解一下生產者消費者模式&#xff1a; 生產者就是生產數據的一方&#xff0c;消費者就是消費數據的另一方。在多線程開發中&#xff0c;如果生產者處理速度很快&#xff0c;而消費者處理速度很慢&#xff0c;那么生產者就必須等待消費者處理完&#xff0c;才能繼…

python生成list的時候 可以用lamda也可以不用_python 可迭代對象,迭代器和生成器,lambda表達式...

分頁查找#5.隨意寫一個20行以上的文件(divmod)# 運行程序&#xff0c;先將內容讀到內存中&#xff0c;用列表存儲。# l []# 提示&#xff1a;一共有多少頁# 接收用戶輸入頁碼&#xff0c;每頁5條&#xff0c;僅輸出當頁的內容def read_page(bk_list,n,endlineNone):startline …

數據挖掘技術簡介[轉]

關鍵詞&#xff1a; 關鍵詞&#xff1a;數據挖掘 數據集合 1. 引言  數據挖掘(Data Mining)是從大量的、不完全的、有噪聲的、模糊的、隨機的數據中提取隱含在其中的、人們事先不知道的、但又是潛在有用的信息和知識的過程。隨…

樹莓派安裝smbus_樹莓派使用smbus不兼容問題(no module named 'smbus')

樹莓派使用smbus不兼容問題(no module named ‘smbus’)python3.5–3.6可以使用smbus2代替smbus1. 先參考以下方法&#xff1a;github討論樹莓派社區2.Pypi上可以下載smbus2smbus2PyPi介紹&#xff1a;當前支持的功能有&#xff1a;獲取i2c功能(I2C_FUNCS)read_bytewrite_byter…

Java并發編程之線程池ThreadPoolExecutor解析

線程池存在的意義 平常使用線程即new Thread()然后調用start()方法去啟動這個線程&#xff0c;但是在頻繁的業務情況下如果在生產環境大量的創建Thread對象是則會浪費資源&#xff0c;不僅增加GC回收壓力&#xff0c;并且還浪費了時間&#xff0c;創建線程是需要花時間的&…

面向過程的門面模式

{*******************************************************}{ }{ 業務邏輯一 }{ }{ 版權所有 (C) 2008 陳…