多個執行緒可以讀一個變數,只有一個執行緒可以對這個變數進行寫,到底要不要加鎖?
其實我認為加鎖的意義也不大。為什麼呢,假如加了鎖,在寫的時候如果有讀的執行緒,那麼需要等待讀完再寫,這時候,這個讀的資料也是寫之前的資料。假設不加鎖,還是讀執行緒先,寫執行緒後執行,這時候很有可能寫會跑到讀的前面優先執行寫的操作。讀的就會是新資料。否則,讀的就是老資料。大多數場景是可以這樣做的。當然,如果資料的時間先後很敏感的,肯定要加鎖
先說結論:
不必要
如果不需要可見性,什麼都不需要加
如果需要保證可見性,則需要加volatile關鍵字。這裡可以加鎖,但是沒必要,對效能有影響
下面簡單解釋下原因:
加鎖是因為操作不是原子性的,以i++這個操作來解釋,看下面兩張圖。
i++這個操作需要
先將i的值從記憶體中讀出來
然後加1
最後寫回去
看上面第二張圖,能很清楚的理解流程吧?
加鎖就是保證上面的三步是一個原子操作。
回到問題,這裡只有一個執行緒寫,實際沒有競爭,所以沒必要加鎖。
但是,看第一張圖,因為有主記憶體和本地記憶體的存在
執行緒先寫入本地記憶體
然後刷入主記憶體
其它記憶體同步主記憶體到工作記憶體
然後從工作記憶體中讀取
一個執行緒寫入後,不能保證其它執行緒立即看到,這就是可見性問題。
加了volatile關鍵字後,會強制操作後同步工作記憶體和主記憶體,保證其它執行緒立刻看到。
要加鎖!我們需要保證的是該變數在多執行緒環境下是執行緒安全的。
典型的read write lock場景,Java中比較經典的
ReentrantReadWriteLock。
另外多說一句,當你不確定要不要加鎖的時候,就是你需要加鎖的時候。
然後你參考MySQL資料庫下Innodb儲存引擎預設級別是RR,防止
髒讀
,即當我們update一條資料的時候,是加了行鎖的,其他的讀執行緒在等待著,所以你需不需要加鎖?
不加鎖會出現什麼問題?
顯然會出現髒讀的問題!
這也是面試中常見的一種問題,以該題為突破口,帶你進入多執行緒的世界。
其實加鎖也很簡單,Java中對該變數進行volatile修飾即可!
不瞭解volatile的同學需要學習了,本質上是為了保證變數操作時候的
記憶體可見性
。
那怎麼保證記憶體可見性呢?
禁止指令重排序、記憶體屏障。
重點看下圖hotspot虛擬機器的實現:
當我們對一個變數i進行volatile修飾後,Java在位元組碼層面是對這個變數進行了
ACC_COLATILE
標記。當我們的JVM虛擬機器看見
ACC_COLATILE
這個位元組碼指令後就會對這個變數加一個lock指令(cpu層面的),還是一個
鎖
的概念。
有的人說考慮程式的吞吐量,在保證最終一致性的前提下可以不加鎖,那我的理解就是你不是在搞事情就是在埋坑,埋一個還不容易被發現的坑。這種坑測試測不出來,線上出問題了還復現不出來,所以為了安全還是加鎖吧~
如果僅僅是你描述的,那不需要加鎖。
如果需要每次讀取最新值,注意加volatile。
如果需要及時拿到最新值,注意使用執行緒通知手段。
看你的需求了,如果你不怕丟資料,或者讀到過期的資料,就不用加鎖,如果你對資料的要求很嚴格的話,那還是要加鎖的
這跟多少個執行緒讀有啥關係,就算是一個執行緒讀,也是讀寫併發呀。至於要不要加鎖,看業務是不是需要嚴格的資料一致性或者合法性。
其實,樓主需要再明確具體化點你的需求。這裡面主要涉及兩個點需要考慮的,一是執行緒互斥,二是執行緒同步。為什麼需要互斥,保護共享的資源。如題所述題中的變數就在多個執行緒共享。場景一:只有一個執行緒可以寫,這種情況下,原子操作也能保證資料一致性,但使用鎖也可以,只是耗點效能。場景二:是否需要控制必須先寫後才能讀,沒有寫操作就等待,等待寫完成後就讀取。如果是,這裡需要考慮執行緒同步,可以考慮使用條件變數,以達到控制執行緒同步。
原則上不用,但是(敲重點):如果這個專案只是你一人的,並且你永遠對它內部邏輯非常清楚,不會因為修改增加邏輯去多個執行緒寫這個變數就不用加鎖,反之這如果是個團隊專案,並且專案邏輯擴充套件會越來越複雜,並且團隊間專案交接交叉頻繁,文件不清,新上手的人不清楚邏輯就會可能對這個變數擴充套件寫邏輯,這種情況就要用鎖,這是要解決專案維護和擴充套件問題,現在n多專案都存在多次交接和擴充套件後混亂問題。
要看變數型別,如果是原子級的,哪就沒問題,不用鎖
Java中該變數用volatile約束,因Java每個執行緒都會將用到的變數copy到現成工作棧中,而修改了記憶體中變數的值,還需要將此值由執行緒工作棧寫入到記憶體對應區域,因為這些資料讀寫操作要多個步驟完成,因此不具有原子性,故需要指定volatile關鍵字修飾變數,讓執行緒對此變數的操作都直接操作記憶體,而不是有中間的對於執行緒棧的複製動作。
原則上要加鎖。個別簡單場景可以不加,比如可以確定讀寫不會衝突,或者單個簡單控制變數考慮volatile。如果是大專案且效能最佳化指標不是納秒級的,建議加上鎖,免得埋坑。
java有讀寫鎖,寫鎖互斥
看設計,看需求。一般讀寫鎖,讀讀共享,讀寫互斥,寫寫互斥。
看變數型別,以32位系統為例,不大於32bit的變數沒有問題,1原子操作,2讀寫匯流排競爭和仲裁
技術上不懂
處理上 寫變數的執行緒正在寫的時候 應當不讓讀變數才行?這個環節是要加鎖的
多讀一寫,我贊同峰子777的做法,原因:從可見性來說,用volatile修飾能保證其他執行緒讀到的是最新值
看你用什麼語言開發的,如果是c/C++,假設讀寫int, int記憶體裡是4Byte,則有可能
寫執行緒寫到2byte,就有一個讀執行緒讀取資料了,則是就是髒資料了。
但一些高階語言,對變數操作基本是原子操作,基本是原子操作。
寫的前後要給變數加鎖啊,而且這樣的場景有專業術語的叫樂觀鎖。
樂觀鎖(寫的前後不能讀),悲觀鎖(讀的前後不能寫)
比如,一場足球比賽,足球的位置是個固定的變數v,張三踢一腳,把球從a踢到b,如果不鎖,所有人收到球的位置可能是a也可能是b,如果寫v的時候鎖上讀的操作,那麼就不會出現誤讀的情況。
不要加鎖,但是要鎖匯流排,加lock字首
加或不加 看情況