為了更好的走讀Java concurrent包源碼,首先普及下基礎知識:volatile、CAS正勒、LockSupport圈驼。
concurrent包的實現(xiàn)示意圖
volatile
保證可見性,但是不保證原子性(i++并發(fā)出問題)
禁止指令重排序
指令重排序推薦閱讀
CAS
簡介
CAS(Compare And Swap))顧名思義為比較并交換。CAS 操作包含三個操作數(shù) —— 內(nèi)存位置(V)、預期原值(A)和新值(B)。如果內(nèi)存位置的值與預期原值相匹配柱锹,那么處理器會自動將該位置值更新為新值哪自。否則,處理器不做任何操作禁熏。無論哪種情況壤巷,它都會在 CAS 指令之前返回該位置的值。CAS 有效地說明了 “ 我認為位置 V 應該包含值 A瞧毙;如果包含該值胧华,則將 B 放到這個位置;否則宙彪,不要更改該位置矩动,只告訴我這個位置現(xiàn)在的值即可∈推幔”
目的
利用CPU的CAS指令悲没,同時借助JNI來完成Java的非阻塞算法。
問題
ABA問題
如果V的值先由A變成B男图,再由B變成A示姿,我們最終看到的是A,但是是發(fā)生變化了逊笆。我們可以通過加時間戳或者版本號來解決栈戳。循環(huán)時間長開銷大
如果JVM能支持處理器提供的pause指令那么效率會有一定的提升。只能保證一個共享變量的原子操作
我們可以通過把多個共享變量合并成一個共享變量來操作览露。比如有兩個共享變量i=2,j=a荧琼,合并一下ij=2a,然后用CAS來操作ij差牛。
溫馨提示:感興趣的同學可以通過AtomicInteger等原子類源碼來了解CAS的具體實踐
LockSupport
LockSupport是用來創(chuàng)建鎖和其他同步類的基本線程阻塞原語。每個使用LockSupport的線程都會與一個許可關聯(lián)堰乔,如果該許可可用偏化,并且可在進程中使用,則調(diào)用park()將會立即返回镐侯,否則可能阻塞侦讨。如果許可尚不可用,則可以調(diào)用 unpark 使其可用苟翻。但是注意許可不可重入韵卤,也就是說只能調(diào)用一次park()方法,否則會一直阻塞崇猫。
LockSupport問題
-
LockSupport.park()和unpark()沈条,與object.wait()和notify()的區(qū)別?
1.1 面向的主體不一樣诅炉。LockSuport主要是針對Thread進進行阻塞處理蜡歹,可以指定阻塞隊列的目標對象屋厘,每次可以指定具體的線程喚醒。Object.wait()是以對象為緯度月而,阻塞當前的線程和喚醒單個(隨機)或者所有線程汗洒。
1.2 實現(xiàn)機制不同。雖然LockSuport可以指定monitor的object對象父款,但和object.wait()溢谤,兩者的阻塞隊列并不交叉『┰埽可以看下測試例子溯香。object.notifyAll()不能喚醒LockSupport的阻塞Thread.
LockSupport能響應Thread.interrupt()事件不?會拋出InterruptedException異常浓恶?
能響應interrupt事件玫坛,但不會拋出InterruptedException異常。
//問題2
public class LockSupportInterrupt {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(new Runnable() {
private int count = 0;
@Override
public void run()
{
long start = System.currentTimeMillis();
long end = 0;
while ((end - start) <= 1000)
{
count++;
end = System.currentTimeMillis();
}
System.out.println("after 1 second.count=" + count);
//等待或許許可
LockSupport.park();
System.out.println("thread over." + Thread.currentThread().isInterrupted());
}
});
t.start();
Thread.sleep(2000);
// 中斷線程
t.interrupt();
System.out.println("main over");
}
}