在看 Java并發(fā)包(java.util.concurrent)中大量使用了CAS操作,CAS即 Compare And Swap(CAS),比較并交換,其中由sun.misc.Unsafe實(shí)現(xiàn),Unsafe類(lèi)提供了硬件級(jí)別的原子操作,Java無(wú)法直接訪問(wèn)到操作系統(tǒng)底層(如系統(tǒng)硬件等),為此Java使用native方法來(lái)擴(kuò)展Java程序的功能。具體實(shí)現(xiàn)使用c++。 牛B,**Unsafe類(lèi)提供了硬件級(jí)別的原子操作**,但到底是什么意思呢?CAS從字面意思上就有兩部分:1、讀取并比較;2、判斷并決定是否交換。然后把這兩步驟作為原子操作,這樣就能保證線程安全
幾句關(guān)于
Unsafe的并發(fā)性。compareAndSwap方法是
原子的,并且可用來(lái)實(shí)現(xiàn)高性能的、無(wú)鎖的數(shù)據(jù)結(jié)構(gòu)。比如,考慮問(wèn)題:在使用大量線程的共享對(duì)象上增長(zhǎng)值。...
Java是一門(mén)安全的編程語(yǔ)言,防止程序員犯很多愚蠢的錯(cuò)誤,它們大部分是基于內(nèi)存管理的。但是,有一種方式可以有意的執(zhí)行一些不安全、容易犯錯(cuò)的操作,那就是使用
Unsafe
類(lèi)。
CAS操作有3個(gè)操作數(shù),內(nèi)存值M,預(yù)期值E,新值U,如果M==E,則將內(nèi)存值修改為B,否則啥都不做。
sun.misc.Unsafe這個(gè)類(lèi)是如此地不安全,以至于JDK開(kāi)發(fā)者增加了很多特殊限制來(lái)訪問(wèn)它。它的構(gòu)造器是私有的,工廠方法
getUnsafe()的調(diào)用器只能被Bootloader加載。如你在下面代碼片段的第8行所見(jiàn),這個(gè)家伙甚至沒(méi)有被任何類(lèi)加載器加載,所以它的類(lèi)加載器是
null。它會(huì)拋出
SecurityException 異常來(lái)阻止侵入者。
public final class Unsafe {
...
private Unsafe() {}
private static final Unsafe theUnsafe = new Unsafe();
...
public static Unsafe getUnsafe() {
Class cc = sun.reflect.Reflection.getCallerClass(2);
if (cc.getClassLoader() != null)
throw new SecurityException("Unsafe");
return theUnsafe;
}
...
public
static
Unsafe getUnsafe() {
try
{
Field f = Unsafe.
class
.getDeclaredField(
"theUnsafe"
);
f.setAccessible(
true
);
return
(Unsafe)f.get(
null
);
}
catch
(Exception e) {
/* ... */
}
}
Unsafe一些有用的特性
- 虛擬機(jī)“集約化”(VM intrinsification):如用于無(wú)鎖Hash表中的CAS(比較和交換)。再比如compareAndSwapInt這個(gè)方法用JNI調(diào)用,包含了對(duì)CAS有特殊引導(dǎo)的本地代碼。在這里你能讀到更多關(guān)于CAS的信息:http://en.wikipedia.org/wiki/Compare-and-swap。
- 主機(jī)虛擬機(jī)(譯注:主機(jī)虛擬機(jī)主要用來(lái)管理其他虛擬機(jī)。而虛擬平臺(tái)我們看到只有g(shù)uest VM)的sun.misc.Unsafe功能能夠被用于未初始化的對(duì)象分配內(nèi)存(用allocateInstance方法),然后將構(gòu)造器調(diào)用解釋為其他方法的調(diào)用。
- 你可以從本地內(nèi)存地址中追蹤到這些數(shù)據(jù)。使用java.lang.Unsafe類(lèi)獲取內(nèi)存地址是可能的。而且可以通過(guò)unsafe方法直接操作這些變量!
- 使用allocateMemory方法,內(nèi)存可以被分配到堆外。例如當(dāng)allocateDirect方法被調(diào)用時(shí)DirectByteBuffer構(gòu)造器內(nèi)部會(huì)使用allocateMemory。
- arrayBaseOffset和arrayIndexScale方法可以被用于開(kāi)發(fā)arraylets,一種用來(lái)將大數(shù)組分解為小對(duì)象、限制掃描的實(shí)時(shí)消耗或者在大對(duì)象上做更新和移動(dòng)。
Unsafe.java是jdk并發(fā)類(lèi)庫(kù)java.util.concurrent包中使用的底層類(lèi),該類(lèi)中的方法都是通過(guò)native方法調(diào)用操作系統(tǒng)命令。
在多線程環(huán)境下,主要存在兩種特殊的場(chǎng)景:
1;多個(gè)線程同時(shí)訪問(wèn)某個(gè)對(duì)象的方法,由于操作系統(tǒng)對(duì)線程調(diào)度的不確定性,存在使該對(duì)象的狀態(tài)處于不一致的狀態(tài),為了避免這種情況的發(fā)生,我們可以聲明該方法為同步方法(synchronized)。保證某一時(shí)刻只有一個(gè)線程調(diào)用該方法,其它調(diào)用線程則被阻塞。這種方法在并發(fā)性很高的情況下會(huì)產(chǎn)生大量的線程調(diào)度開(kāi)銷(xiāo)。從而影響程序的并發(fā)性能。在java.util.concurrent包中通過(guò)使用非阻塞原子方法,減少操作系統(tǒng)的無(wú)用線程調(diào)度開(kāi)銷(xiāo),從而提高并發(fā)性能。
2;多線程通過(guò)某個(gè)對(duì)象進(jìn)行通信,即常見(jiàn)的生產(chǎn)者消費(fèi)者模型。在以前的jdk版本中是通過(guò)wait,notify方法實(shí)現(xiàn)的。該方法也是通過(guò)底層在某個(gè)信號(hào)量上的阻塞隊(duì)列實(shí)現(xiàn)的。而Unsafe類(lèi)中直接提供操作系統(tǒng)調(diào)度命令park,unpark,減少信號(hào)量的開(kāi)銷(xiāo),提高新能。
從上面可以看出,Unsafe類(lèi)是通過(guò)提供底層的操作系統(tǒng)命令接口給開(kāi)發(fā)者,從而提供程序的性能。