並發之volatile底層原理
12.Java多線程-java.util.concurrent.atomic包原理解讀
11.volatile底層實現原理
===================
12.Java多線程-java.util.concurrent.atomic包原理解讀
參考學習:http://blog.csdn.net/zhangerqing/article/details/43057799 多線程基本類型AtomicReference
Atomic*
Atomic包是java.util.concurrent下的另一個專門為線程安全設計的Java包,包含多個原子操作類,但Atomic的線程安全是如何來實現的呢?
1、硬件同步策略
現在的處理器都支持多重處理,當然也包含多個處理器共享外圍設備和內存,同時,加強了指令集以支持一些多處理的特殊需求。
特別是幾乎所有的處理器都可以將其他處理器阻塞以便更新共享變量
2、Compare and swap(CAS)
當前的處理器基本都支持CAS,只不過每個廠家所實現的算法並不一樣罷了,每一個CAS操作過程都包含三個運算符:一個內存地址V,
一個期望的值A和一個新值B,操作的時候如果這個地址上存放的值等於這個期望的值A,則將地址上的值賦為新值B,否則不做任何操作。
CAS的基本思路就是,如果這個地址上的值和期望的值相等,則給其賦予新值,否則不做任何事兒,但是要返回原值是多少。我們來看一個例子,
解釋CAS的實現過程(並非真實的CAS實現)
3、實現的核心源碼:
/**
* Atomically increments by one the current value.
*
* @return the updated value
*/
public final int incrementAndGet() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return next;
}
}
public final boolean compareAndSet(int expect, int update) {
//使用unsafe的native方法,實現高效的硬件級別CAS
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
他比直接使用傳統的java鎖機制(阻塞的)有什麽好處?
最大的好處就是可以避免多線程的優先級倒置和死鎖情況的發生,當然高並發下的性能提升也是很重要的
4、CAS線程安全
說了半天,我們要回歸到最原始的問題了:這樣怎麽實現線程安全呢?請大家自己先考慮一下這個問題,其實我們在語言層面是沒有做任何同步的操作的,
大家也可以看到源碼沒有任何鎖加在上面,可它為什麽是線程安全的呢?這就是Atomic包下這些類的奧秘:語言層面不做處理,我們將其交給硬件—CPU和內存,
利用CPU的多處理能力,實現硬件層面的阻塞,再加上volatile變量的特性即可實現基於原子操作的線程安全。所以說,CAS並不是無阻塞,
只是阻塞並非在語言、線程方面,而是在硬件層面,所以無疑這樣的操作會更快更高效!
5總結
雖然基於CAS的線程安全機制很好很高效,但要說的是,並非所有線程安全都可以用這樣的方法來實現,這只適合一些粒度比較小,
如計數器這樣的需求用起來才有效,否則也不會有鎖的存在了
11.volatile底層實現原理
定義
java編程語言允許線程訪問共享變量,為了確保共享變量能夠被準確和一致的更新,線程應該通過排他鎖獲得這個變量。java提供了volatile,在某些情況下比鎖更加方便。如果一個字段被聲明成volatile,java線程內存模型確保所有線程看到的這個變量的值是一致的。
匯編代碼
使用命令獲得匯編代碼
java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly
Java HotSpot(TM) 64-Bit Server VM warning: PrintAssembly is enabled; turning on DebugNonSafepoints to gain additional output
Loaded disassembler from /Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home/jre/lib/hsdis-amd64.dylib
Decoding compiled method 0x0000000110da4b50:
Code:
[Disassembling for mach=‘i386:x86-64‘]
[Entry Point]
[Constants]
# {method} {0x000000010f163000} ‘hashCode‘ ‘()I‘ in ‘java/lang/String‘
# [sp+0x40] (sp of caller)
0x0000000110da4cc0: mov 0x8(%rsi),%r10d
0x0000000110da4cc4: shl $0x3,%r10
0x0000000110da4cc8: cmp %rax,%r10
0x0000000110da4ccb: jne 0x0000000110ceae20 ; {runtime_call}
0x0000000110da4cd1: data32 data32 nopw 0x0(%rax,%rax,1)
0x0000000110da4cdc: data32 data32 xchg %ax,%ax
[Verified Entry Point]
0x0000000110da4ce0: mov %eax,-0x14000(%rsp)
0x0000000110da4ce7: push %rbp
0x0000000110da4ce8: sub $0x30,%rsp
......
mac系統下使用此命令的前提是下載hsdis-amd64.dylib,並將其放入到jdk的jre下的lib目錄下
實現原理
通過利用工具獲得class文件的匯編代碼,會發現,標有volatile的變量在進行寫操作時,會在前面加上lock質量前綴。
而lock指令前綴會做如下兩件事
-
將當前處理器緩存行的數據寫回到內存。lock指令前綴在執行指令的期間,會產生一個lock信號,lock信號會保證在該信號期間會獨占任何共享內存。lock信號一般不鎖總線,而是鎖緩存。因為鎖總線的開銷會很大。
-
將緩存行的數據寫回到內存的操作會使得其他CPU緩存了該地址的數據無效。
並發之volatile底層原理