? ? ? ? ? ?volatile作為一個輕量級鎖機制,首先保證了可見性,處理器使用MESI(修改、獨占、共享、無效)緩存一致性協議來保證可見性,當多個線程修改數據時,先到達的會使用Lock前綴指令鎖住主緩存的緩存行,緩存行就是我們數據存在緩存里的最小單位,修改自己的數據之后,其它線程通過嗅探技術發現主存的數據修改,會無效化自己的緩存,使得下次取數據的時候從主存里取數據
?注意兩點:
一:happens-before原則:假如一寫一讀兩個線程同時操作數據,寫優先
二:兩個線程同時寫會有偽共享問題
偽共享問題:
? ? ? ? 當兩個線程同時修改同一個緩存行的數據的時候,因為先到達會鎖住緩存行,那么第二個到達的依舊會等待,類似于synchronized悲觀鎖,比如線程A修改緩存行的A數據,線程B修改同一個緩存行的B數據,就會變成串行修改,也就是偽共享問題
怎么解決?
? ? ? ? 當下采用的多是使不同變量在不同緩存行,即如果數據占不了一個緩存行,那么填充緩存行,把緩存行填滿,使不同的數據不再同一個緩存行中,比如@Contended注解,concurrentHashMap就是這樣用的,還有Disruptor框架采用填充緩存行的方式,有興趣的可以去了解下
不保證原子性
? ? ? ? 對于i++操作,這個i++是分為了三步驟,取值、加、設值,當線程A取得主存的值i=10,線程B取得值為10,線程A加1,設置回主存,線程B也加1,設置回主存,兩個線程加完之后,數據依舊為1,所以不保證原子性
特別注意:
? ? ? ? 很多人對這個可見性有所疑惑,認為線程B修改到主存之后,就會讓線程A知道,線程A重新去取值,注意這里并不是,線程A已經取過值之后,其它值修改了主存,他不會去取值,而是自己進行加的操作,這也就是沒有原子性的原因,如果線程A會去重新取值的話,那么以新的值來操作,不就是有原子性了嗎
? ? ? ? volatile的第二個作用是插入內存屏障禁止指令重排序,雖然保證了一些數據安全,但是效率變低