java 原子锁
研究ThreadPoolExecutor的時(shí)候,發(fā)現(xiàn)其中大量使用了volatile變量。?
不知為何,因此做了一番查找,研究:?
其中借鑒了很多網(wǎng)上資料。?
在了解volatile變量作用前,先需要明白一些概念:?
什么是原子操作??
所謂原子操作,就是"不可中斷的一個(gè)或一系列操作" , 在確認(rèn)一個(gè)操作是原子的情況下,多線程環(huán)境里面,我們可以避免僅僅為保護(hù)這個(gè)操作在外圍加上性能昂貴的鎖,甚至借助于原子操作,我們可以實(shí)現(xiàn)互斥鎖。?
很多操作系統(tǒng)都為int類型提供了+-賦值的原子操作版本,比如 NT 提供了 InterlockedExchange 等API, Linux/UNIX也提供了atomic_set 等函數(shù)。?
關(guān)于java中的原子性??
原子性可以應(yīng)用于除long和double之外的所有基本類型之上的“簡單操作”。對(duì)于讀取和寫入出long和double之外的基本類型變量這樣的操作,可以保證它們會(huì)被當(dāng)作不可分(原子)的操作來操作。?
因?yàn)镴VM的版本和其它的問題,其它的很多操作就不好說了,比如說++操作在C++中是原子操作,但在Java中就不好說了。?
另外,Java提供了AtomicInteger等原子類。再就是用原子性來控制并發(fā)比較麻煩,也容易出問題。?
volatile原理是什么??
Java中volatile關(guān)鍵字原義是“不穩(wěn)定、變化”的意思?
使用volatile和不使用volatile的區(qū)別在于JVM內(nèi)存主存和線程工作內(nèi)存的同步之上。volatile保證變量在線程工作內(nèi)存和主存之間一致。?
其實(shí)是告訴處理器, 不要將我放入工作內(nèi)存, 請(qǐng)直接在主存操作我.?
接下來是測試 :(通過測試能更好的發(fā)現(xiàn)和分析問題)?
申明了幾種整形的變量,開啟100個(gè)線程同時(shí)對(duì)這些變量進(jìn)行++操作,發(fā)現(xiàn)結(jié)果差異很大:?
>>Execute End:?
>>Atomic: 100000?
>>VInteger: 38790?
>>Integer: 68749?
>>Source i: 99205?
>>Source Vi: 99286?
也就是說除了Atomic,其他的都是錯(cuò)誤的。?
我們通過一些疑問,來解釋一下。?
1:為什么會(huì)產(chǎn)生錯(cuò)誤的數(shù)據(jù)??
多線程引起的,因?yàn)閷?duì)于多線程同時(shí)操作一個(gè)整型變量在大并發(fā)操作的情況下無法做到同步,而Atom提供了很多針對(duì)此類線程安全問題的解決方案,因此解決了同時(shí)讀寫操作的問題。?
2:為什么會(huì)造成同步問題??
Java多線程在對(duì)變量進(jìn)行操作的時(shí)候,實(shí)際上是每個(gè)線程會(huì)單獨(dú)分配一個(gè)針對(duì)i值的拷貝(獨(dú)立內(nèi)存區(qū)域),但是申明的i值確是在主內(nèi)存區(qū)域中,當(dāng)對(duì)i值修改完畢后,線程會(huì)將自己內(nèi)存區(qū)域塊中的i值拷貝到主內(nèi)存區(qū)域中,因此有可能每個(gè)線程拿到的i值是不一樣的,從而出現(xiàn)了同步問題。?
3:為什么使用volatile修飾integer變量后,還是不行??
因?yàn)関olatile僅僅只是解決了存儲(chǔ)的問題,即i值只是保留在了一個(gè)內(nèi)存區(qū)域中,但是i++這個(gè)操作,涉及到獲取i值、修改i值、存儲(chǔ)i值(i=i+1),這里的volatile只是解決了存儲(chǔ)i值得問題,至于獲取和修改i值,確是沒有做到同步。?
4:既然不能做到同步,那為什么還要用volatile這種修飾符??
主要的一個(gè)原因是方便,因?yàn)橹恍杼砑右粋€(gè)修飾符即可,而無需做對(duì)象加鎖、解鎖這么麻煩的操作。但是本人不推薦使用這種機(jī)制,因?yàn)楸容^容易出問題(臟數(shù)據(jù)),而且也保證不了同步。?
5:那到底如何解決這樣的問題??
第一種:采用同步synchronized解決,這樣雖然解決了問題,但是也降低了系統(tǒng)的性能。?
第二種:采用原子性數(shù)據(jù)Atomic變量,這是從JDK1.5開始才存在的針對(duì)原子性的解決方案,這種方案也是目前比較好的解決方案了。?
6:Atomic的實(shí)現(xiàn)基本原理??
首先Atomic中的變量是申明為了volatile變量的,這樣就保證的變量的存儲(chǔ)和讀取是一致的,都是來自同一個(gè)內(nèi)存塊,然后Atomic提供了getAndIncrement方法,該方法對(duì)變量的++操作進(jìn)行了封裝,并提供了compareAndSet方法,來完成對(duì)單個(gè)變量的加鎖和解鎖操作,方法中用到了一個(gè)UnSafe的對(duì)象,現(xiàn)在還不知道這個(gè)UnSafe的工作原理(似乎沒有公開源代碼)。Atomic雖然解決了同步的問題,但是性能上面還是會(huì)有所損失,不過影響不大,網(wǎng)上有針對(duì)這方面的測試,大概50million的操作對(duì)比是250ms : 850ms,對(duì)于大部分的高性能應(yīng)用,應(yīng)該還是夠的了。?
Java代碼?? package?qflag.ucstar.test.thread;?? ?? import?java.util.concurrent.atomic.AtomicInteger;?? ?? /**? ?*?測試原子性的同步? ?*?@author?polarbear?2009-3-14? ?*? ?*/?? public?class?TestAtomic?{?? ?????? ????public?static?AtomicInteger?astom_i?=?new?AtomicInteger();?? ?????? ????public?static?volatile?Integer?v_integer_i?=?0;?? ?????? ????public?static?volatile?int?v_i?=?0;?? ?????? ????public?static?Integer?integer_i?=?0;?? ?????? ????public?static?int?i?=?0;?? ?????? ????public?static?int?endThread?=?0;?? ?????? ????public?static?void?main(String[]?args)?{?? ????????new?TestAtomic().testAtomic();?? ????}?? ?????? ????public?void?testAtomic()?{?? ?????????? ????????for(int?i=0;?i<100;?i++)?{?? ????????????new?Thread(new?IntegerTestThread()).start();?? ????????}?? ?????????? ????????try?{?? ????????????for(;;)?{?? ????????????????Thread.sleep(500);?? ????????????????if(TestAtomic.endThread?==?100)?{?? ????????????????????System.out.println(">>Execute?End:");?? ????????????????????System.out.println(">>Atomic:?\t"+TestAtomic.astom_i);?? ????????????????????System.out.println(">>VInteger:?\t"+TestAtomic.v_integer_i);?? ????????????????????System.out.println(">>Integer:?\t"+TestAtomic.integer_i);?? ????????????????????System.out.println(">>Source?i:?\t"+TestAtomic.i);?? ????????????????????System.out.println(">>Source?Vi:?\t"+TestAtomic.v_i);?? ????????????????????break;?? ????????????????}?? ????????????}?? ?????????????? ????????}?catch?(Exception?e)?{?? ????????????e.printStackTrace();?? ????????}?? ????}?? ?????? }?? class?IntegerTestThread?implements?Runnable?{?? ????public?void?run()?{?? ????????int?x?=?0;?? ????????while(x<1000)?{?? ????????????TestAtomic.astom_i.incrementAndGet();?? ????????????TestAtomic.v_integer_i++;?? ????????????TestAtomic.integer_i++;?? ????????????TestAtomic.i++;?? ????????????TestAtomic.v_i++;?? ????????????x++;?? ????????}?? ????????++TestAtomic.endThread;?? ????}?? } ?
不知為何,因此做了一番查找,研究:?
其中借鑒了很多網(wǎng)上資料。?
在了解volatile變量作用前,先需要明白一些概念:?
什么是原子操作??
所謂原子操作,就是"不可中斷的一個(gè)或一系列操作" , 在確認(rèn)一個(gè)操作是原子的情況下,多線程環(huán)境里面,我們可以避免僅僅為保護(hù)這個(gè)操作在外圍加上性能昂貴的鎖,甚至借助于原子操作,我們可以實(shí)現(xiàn)互斥鎖。?
很多操作系統(tǒng)都為int類型提供了+-賦值的原子操作版本,比如 NT 提供了 InterlockedExchange 等API, Linux/UNIX也提供了atomic_set 等函數(shù)。?
關(guān)于java中的原子性??
原子性可以應(yīng)用于除long和double之外的所有基本類型之上的“簡單操作”。對(duì)于讀取和寫入出long和double之外的基本類型變量這樣的操作,可以保證它們會(huì)被當(dāng)作不可分(原子)的操作來操作。?
因?yàn)镴VM的版本和其它的問題,其它的很多操作就不好說了,比如說++操作在C++中是原子操作,但在Java中就不好說了。?
另外,Java提供了AtomicInteger等原子類。再就是用原子性來控制并發(fā)比較麻煩,也容易出問題。?
volatile原理是什么??
Java中volatile關(guān)鍵字原義是“不穩(wěn)定、變化”的意思?
使用volatile和不使用volatile的區(qū)別在于JVM內(nèi)存主存和線程工作內(nèi)存的同步之上。volatile保證變量在線程工作內(nèi)存和主存之間一致。?
其實(shí)是告訴處理器, 不要將我放入工作內(nèi)存, 請(qǐng)直接在主存操作我.?
接下來是測試 :(通過測試能更好的發(fā)現(xiàn)和分析問題)?
申明了幾種整形的變量,開啟100個(gè)線程同時(shí)對(duì)這些變量進(jìn)行++操作,發(fā)現(xiàn)結(jié)果差異很大:?
>>Execute End:?
>>Atomic: 100000?
>>VInteger: 38790?
>>Integer: 68749?
>>Source i: 99205?
>>Source Vi: 99286?
也就是說除了Atomic,其他的都是錯(cuò)誤的。?
我們通過一些疑問,來解釋一下。?
1:為什么會(huì)產(chǎn)生錯(cuò)誤的數(shù)據(jù)??
多線程引起的,因?yàn)閷?duì)于多線程同時(shí)操作一個(gè)整型變量在大并發(fā)操作的情況下無法做到同步,而Atom提供了很多針對(duì)此類線程安全問題的解決方案,因此解決了同時(shí)讀寫操作的問題。?
2:為什么會(huì)造成同步問題??
Java多線程在對(duì)變量進(jìn)行操作的時(shí)候,實(shí)際上是每個(gè)線程會(huì)單獨(dú)分配一個(gè)針對(duì)i值的拷貝(獨(dú)立內(nèi)存區(qū)域),但是申明的i值確是在主內(nèi)存區(qū)域中,當(dāng)對(duì)i值修改完畢后,線程會(huì)將自己內(nèi)存區(qū)域塊中的i值拷貝到主內(nèi)存區(qū)域中,因此有可能每個(gè)線程拿到的i值是不一樣的,從而出現(xiàn)了同步問題。?
3:為什么使用volatile修飾integer變量后,還是不行??
因?yàn)関olatile僅僅只是解決了存儲(chǔ)的問題,即i值只是保留在了一個(gè)內(nèi)存區(qū)域中,但是i++這個(gè)操作,涉及到獲取i值、修改i值、存儲(chǔ)i值(i=i+1),這里的volatile只是解決了存儲(chǔ)i值得問題,至于獲取和修改i值,確是沒有做到同步。?
4:既然不能做到同步,那為什么還要用volatile這種修飾符??
主要的一個(gè)原因是方便,因?yàn)橹恍杼砑右粋€(gè)修飾符即可,而無需做對(duì)象加鎖、解鎖這么麻煩的操作。但是本人不推薦使用這種機(jī)制,因?yàn)楸容^容易出問題(臟數(shù)據(jù)),而且也保證不了同步。?
5:那到底如何解決這樣的問題??
第一種:采用同步synchronized解決,這樣雖然解決了問題,但是也降低了系統(tǒng)的性能。?
第二種:采用原子性數(shù)據(jù)Atomic變量,這是從JDK1.5開始才存在的針對(duì)原子性的解決方案,這種方案也是目前比較好的解決方案了。?
6:Atomic的實(shí)現(xiàn)基本原理??
首先Atomic中的變量是申明為了volatile變量的,這樣就保證的變量的存儲(chǔ)和讀取是一致的,都是來自同一個(gè)內(nèi)存塊,然后Atomic提供了getAndIncrement方法,該方法對(duì)變量的++操作進(jìn)行了封裝,并提供了compareAndSet方法,來完成對(duì)單個(gè)變量的加鎖和解鎖操作,方法中用到了一個(gè)UnSafe的對(duì)象,現(xiàn)在還不知道這個(gè)UnSafe的工作原理(似乎沒有公開源代碼)。Atomic雖然解決了同步的問題,但是性能上面還是會(huì)有所損失,不過影響不大,網(wǎng)上有針對(duì)這方面的測試,大概50million的操作對(duì)比是250ms : 850ms,對(duì)于大部分的高性能應(yīng)用,應(yīng)該還是夠的了。?
Java代碼??
總結(jié)
- 上一篇: 云服务器搭建百度贴吧自动签到(贴吧云签到
- 下一篇: 人生的诗意