菠蘿大象
用心思考,用心寫(xiě)作
首頁(yè)
新隨筆
聯(lián)系
管理
隨筆-67 評(píng)論-522 文章-0 trackbacks-0
淺談volatile變量的理解
在
Java
并發(fā)編程里面,
volatile
是個(gè)很重要的概念,大象也來(lái)講講自己對(duì)它的理解。
以前曾經(jīng)有段時(shí)間我一直沒(méi)搞明白
volatile
到底怎么用,它是怎樣實(shí)現(xiàn)的同步,而且對(duì)于
volatile
變量還有一些限制條件。任何技術(shù)在沒(méi)完全弄明白之前,至少在沒(méi)熟練掌握之前都不太敢放心大膽的用,大象想將自己對(duì)它的理解分享出來(lái),給需要的人一些幫助。
volatile
是輕量級(jí)的鎖,它只具備可見(jiàn)性,但沒(méi)有原子特性。用
volatile
聲明的變量,它的同步特性,簡(jiǎn)單來(lái)講就是對(duì)該變量的單個(gè)讀
/
寫(xiě)是同步的。這是什么意思呢?我還是以共享變量
i
為例,不過(guò)在
i
的前面加上了
volatile
修飾符。
private
volatile
int
i
= 0;
public
int
get() {
return
i
;
}
public
void
set(
int
i) {
this
.
i
= i;
}
為了與傳統(tǒng)的
getXXX
和
setXXX
方法區(qū)別開(kāi)來(lái),我將方法名改成了上面這樣。對(duì)于
get
和
set
方法,如果有多個(gè)線程同時(shí)訪問(wèn),
volatile
是可以保證
i
的原子性的,再簡(jiǎn)單點(diǎn)講,對(duì)于變量
i
,
get
和
set
方法是同步的。這是通過(guò)什么來(lái)保證的呢?是通過(guò)
Java
語(yǔ)言規(guī)范
:
如果一個(gè)字段被聲明為
volatile
,
Java
內(nèi)存模型確保所有線程看到這個(gè)變量的值是一致的。
看到這里,可能有的童鞋會(huì)想,既然
volatile
可以保證內(nèi)存可見(jiàn)性,那不就解決了淺談
Java
共享變量這篇文章里面講到的共享變量的并發(fā)問(wèn)題嗎?只要在
i
的前面加上
volatile
就可以解決同步問(wèn)題了,你確定?實(shí)踐是最好的辦法,動(dòng)手試下,看看結(jié)果如何。
事實(shí)證明這個(gè)辦法行不通,為什么呢?原因出在
i++
上面,增量操作符
++
不是原子的。這個(gè)操作分解開(kāi)來(lái)看是先從堆內(nèi)存中獲得
i
值的副本放到緩存中,然后對(duì)副本值加
1
,最后再將副本值寫(xiě)回到堆內(nèi)存的變量
i
中。從這個(gè)過(guò)程我們可以看到,從堆內(nèi)存中獲得
i(get
方法
)
以及將值寫(xiě)回到
i(set
方法
)
這兩步都是同步的,但中間的就不能保證是同步的了。
對(duì)于
volatile
修飾的變量,只保證了他的可見(jiàn)性,但不保證原子性。最常用的應(yīng)該是
boolean
類(lèi)型,它用來(lái)作為狀態(tài)標(biāo)志,因?yàn)樗挥?/span>
true
和
false
兩個(gè)值,不會(huì)有非原子性的操作。當(dāng)然不是說(shuō)只能用在布爾類(lèi)型變量上面,其它的基本類(lèi)型和對(duì)象類(lèi)型都可以用。但一定需要小心謹(jǐn)慎的處理,以免掉進(jìn)并發(fā)陷阱而不知。比如
volatile
變量就不適合用于不變性條件這種情況,以上下限為例,
lower
必須小于
upper
,這就是一種不變性條件,你可以理解為這是一種規(guī)則限制。它們都只有
set
與
get
方法,但在
set
方法里面加入了約束條件,這時(shí),
volatile
的可見(jiàn)性就不能保證并發(fā)時(shí),
lower
與
upper
之間的不變性條件
(lower<upper)
一定成立了。
/*
* volatile
只保證
lower
與
upper
的最后寫(xiě)入一定會(huì)被其它讀取的線程看到
*
但不能保證在
lower
或
upper
寫(xiě)入時(shí),另一個(gè)變量的值沒(méi)有發(fā)生變化
*/
private
volatile
int
lower
;
private
volatile
int
upper
;
public
int
getLower() {
return
lower
;
}
public
int
getUpper() {
return
upper
;
}
public
void
setLower(
int
lower) {
if
(lower >
upper
)
throw
new
IllegalArgumentException();
this
.
lower
= lower;
}
public
void
setUpper(
int
upper) {
if
(upper <
lower
)
throw
new
IllegalArgumentException();
this
.
upper
= upper;
}
如果
lower
和
upper
的初始值為
0
和
10
,同一時(shí)刻,線程
1
調(diào)用
setLower(8)
,線程
2
調(diào)用
setUpper(2)
,執(zhí)行上完全沒(méi)問(wèn)題,但是現(xiàn)在的
lower
和
upper
的值就變?yōu)榱?/span>
8
和
2
這種無(wú)效的數(shù)據(jù)了,所以
volatile
只能確保可見(jiàn)性,不能確保原子性。
所以在使用
volatile
變量時(shí),請(qǐng)考慮是否滿(mǎn)足下面這樣的要求:
1
、對(duì)變量的寫(xiě)入操作不依賴(lài)變量的當(dāng)前值
(i++
這種操作就不行
)
2
、沒(méi)有用于其它變量的不變式條件中
(lower<upper)
到這里,我們已經(jīng)明白了:用
volatile
修飾的變量只具備可見(jiàn)性,那么它是怎么保證可見(jiàn)性的呢?
現(xiàn)在大家用的電腦
CPU
基本上都是多核的,至少兩核,緩存也有很多級(jí)
(L1
、
L2
、
L3)
。代碼在
JVM
里面執(zhí)行的時(shí)候,
JVM
如果發(fā)現(xiàn)有
CPU
在處理
volatile
變量的寫(xiě)入操作,就會(huì)告訴該
CPU
將當(dāng)前緩存中的數(shù)據(jù)寫(xiě)回到堆內(nèi)存中,但這時(shí)其它
CPU
的緩存值還是舊的,再執(zhí)行操作就會(huì)有問(wèn)題,所以在處理器的內(nèi)部實(shí)現(xiàn)了緩存一致性協(xié)議,當(dāng)有緩存中的數(shù)據(jù)寫(xiě)回內(nèi)存時(shí)會(huì)引起其它
CPU
里這個(gè)
volatile
變量的緩存值無(wú)效,如果這時(shí)候其它
CPU
要想使用就必須到堆內(nèi)存中重新讀取該值,這樣就實(shí)現(xiàn)了
volatile
變量的可見(jiàn)性。我這樣講方便大家理解,實(shí)際的情況比這復(fù)雜的多。
以上是大象關(guān)于
volatile
變量的一些淺薄見(jiàn)解,真的很淺,大象學(xué)藝不精,有什么不對(duì)的,還請(qǐng)各位指出來(lái)。
謝謝!
本文為菠蘿大象原創(chuàng),如要轉(zhuǎn)載請(qǐng)注明出處。
http://www.tkk7.com/
bolo
posted on 2014-06-20 17:08
菠蘿大象
閱讀(5979)
評(píng)論(2)
編輯
收藏
所屬分類(lèi):
Concurrency
評(píng)論:
#
re: 淺談volatile變量的理解[未登錄](méi) 2014-06-22 21:20 |
星情
比 ibm devloper works 上的那篇關(guān)于 volatitle 的文章更易于理解
回復(fù)
更多評(píng)論
#
re: 淺談volatile變量的理解
2014-07-18 19:58 |
zhangchao
簡(jiǎn)單來(lái)說(shuō),volatile就是告訴程序,該變量是易變的,不穩(wěn)定的,每次必須去主存讀取,而不要從自己的緩存中獲取副本。
回復(fù)
更多評(píng)論
新用戶(hù)注冊(cè)
刷新評(píng)論列表
只有注冊(cè)用戶(hù)
登錄
后才能發(fā)表評(píng)論。
網(wǎng)站導(dǎo)航:
博客園
IT新聞
Chat2DB
C++博客
博問(wèn)
管理
相關(guān)文章:
淺談Java兩種并發(fā)類(lèi)型——計(jì)算密集型與IO密集型
淺談volatile變量的理解
淺談Java共享變量
<
2014年6月
>
日
一
二
三
四
五
六
25
26
27
28
29
30
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1
2
3
4
5
隨筆分類(lèi)
(67)
Concurrency(3)
Database(4)
dom4j&jdom(3)
Eclipse(3)
Flex(4)
Hibernate(2)
Java(11)
jBPM(1)
jQuery(5)
Linux(1)
mybatis-generator(3)
Spring3(11)
Struts2(9)
隨筆(7)
隨筆檔案
(67)
2015年9月 (1)
2015年8月 (1)
2015年7月 (1)
2015年5月 (1)
2015年4月 (1)
2015年3月 (3)
2015年2月 (1)
2015年1月 (2)
2014年8月 (1)
2014年6月 (2)
2014年5月 (2)
2014年4月 (3)
2014年3月 (1)
2014年2月 (1)
2014年1月 (2)
2013年12月 (3)
2013年11月 (2)
2013年10月 (2)
2013年9月 (2)
2012年5月 (1)
2012年4月 (2)
2011年8月 (1)
2011年6月 (1)
2011年5月 (3)
2010年9月 (1)
2010年8月 (1)
2010年7月 (1)
2010年6月 (1)
2010年5月 (2)
2010年4月 (4)
2010年3月 (2)
2010年2月 (7)
2008年9月 (2)
2008年8月 (6)
搜索
積分與排名
積分 - 780926
排名 - 54
最新隨筆
1.?由Struts2Action代理和googlecode-jsonplugin產(chǎn)生的一個(gè)IllegalArgumentException異常問(wèn)題
2.?在CentOS系統(tǒng)中同步時(shí)間
3.?全局唯一ID生成器淺析
4.?用Java實(shí)現(xiàn)MJD轉(zhuǎn)UTC
5.?修改mybatis-generator-1.3.2源碼實(shí)現(xiàn)自定義代碼生成詳解(三)
6.?修改mybatis-generator-1.3.2源碼實(shí)現(xiàn)自定義代碼生成詳解(二)
7.?修改mybatis-generator-1.3.2源碼實(shí)現(xiàn)自定義代碼生成詳解(一)
8.?Quartz Spring與Spring Task總結(jié)
9.?常用的MySQL復(fù)雜查詢(xún)語(yǔ)句寫(xiě)法
10.?常用的MySQL語(yǔ)句寫(xiě)法
11.?淺談Java兩種并發(fā)類(lèi)型——計(jì)算密集型與IO密集型
12.?使用XStream注解實(shí)現(xiàn)Java對(duì)象與XML互相轉(zhuǎn)換的代碼示例
13.?淺談volatile變量的理解
14.?淺談Java共享變量
15.?m2eclipse安裝小貼士
最新評(píng)論
1.?re: 修改mybatis-generator-1.3.2源碼實(shí)現(xiàn)自定義代碼生成詳解(一)
評(píng)論內(nèi)容較長(zhǎng),點(diǎn)擊標(biāo)題查看
--半湖思絮
2.?re: 修改mybatis-generator-1.3.2源碼實(shí)現(xiàn)自定義代碼生成詳解(一)
評(píng)論內(nèi)容較長(zhǎng),點(diǎn)擊標(biāo)題查看
--菠蘿大象
3.?re: 修改mybatis-generator-1.3.2源碼實(shí)現(xiàn)自定義代碼生成詳解(一)
評(píng)論內(nèi)容較長(zhǎng),點(diǎn)擊標(biāo)題查看
--donghc
4.?re: 修改mybatis-generator-1.3.2源碼實(shí)現(xiàn)自定義代碼生成詳解(一)
@donghc
maven不是這樣玩的呀,我的run是打包后用的,你仔細(xì)看了第三篇的內(nèi)容么?在IDE里直接啟動(dòng)StartUp就行了,這個(gè)類(lèi)是用來(lái)測(cè)試的
--菠蘿大象
5.?re: 修改mybatis-generator-1.3.2源碼實(shí)現(xiàn)自定義代碼生成詳解(一)
評(píng)論內(nèi)容較長(zhǎng),點(diǎn)擊標(biāo)題查看
--donghc
閱讀排行榜
1.?使用jackson對(duì)Java對(duì)象與JSON字符串相互轉(zhuǎn)換的一些總結(jié)(59337)
2.?在Eclipse中反編譯Class文件完全詳解(43597)
3.?Quartz Spring與Spring Task總結(jié)(38576)
4.?使用XStream注解實(shí)現(xiàn)Java對(duì)象與XML互相轉(zhuǎn)換的代碼示例(33139)
5.?Spring MVC 3.0.5+Spring 3.0.5+MyBatis3.0.4全注解實(shí)例詳解(二)(32454)
評(píng)論排行榜
1.?Struts2.1.6+Spring2.5.6+Hibernate3.3.1全注解實(shí)例詳解(五)(69)
2.?Struts2.1.6+Spring2.5.6+Hibernate3.3.1全注解實(shí)例詳解(三)(47)
3.?Struts2.1.6+Spring2.5.6+Hibernate3.3.1全注解實(shí)例詳解(一)(35)
4.?Struts2.1.6+Spring2.5.6+Hibernate3.3.1全注解實(shí)例詳解(二)(27)
5.?Spring MVC 3.0.5+Spring 3.0.5+MyBatis3.0.4全注解實(shí)例詳解(三)(26)
Powered by:
博客園
模板提供:
滬江博客
Copyright ©2025 菠蘿大象
主站蜘蛛池模板:
亚洲午夜一区二区三区
|
亚洲av乱码一区二区三区
|
成人亚洲综合天堂
|
久久永久免费人妻精品
|
a级毛片毛片免费观看永久
|
国产精品免费在线播放
|
亚洲最大无码中文字幕
|
亚洲无线一二三四区
|
亚洲激情在线视频
|
自怕偷自怕亚洲精品
|
亚洲AV无码一区二区乱孑伦AS
|
亚洲日韩乱码久久久久久
|
久久久久久亚洲av成人无码国产
|
成人免费区一区二区三区
|
亚洲视频在线免费
|
午夜成人无码福利免费视频
|
老湿机一区午夜精品免费福利
|
亚洲日韩看片无码电影
|
亚洲 欧洲 视频 伦小说
|
亚洲AV成人无码天堂
|
亚洲精品电影在线
|
亚洲欧洲国产视频
|
亚洲视屏在线观看
|
在线亚洲精品自拍
|
亚洲国产精品成人一区
|
又大又黄又粗又爽的免费视频
|
亚洲一久久久久久久久
|
亚洲日本人成中文字幕
|
亚洲 日韩 色 图网站
|
亚洲国产精品张柏芝在线观看
|
久久精品亚洲一区二区
|
亚洲精品国产综合久久一线
|
久久青草免费91线频观看不卡
|
亚洲AV午夜福利精品一区二区
|
国产精品福利片免费看
|
一边摸一边桶一边脱免费视频
|
久久www免费人成看国产片
|
天堂在线免费观看
|
美女内射无套日韩免费播放
|
1000部啪啪未满十八勿入免费
|
国产精品成人观看视频免费
|