減少上下文切換 (Lmbench3 時長、vmstat 次數)
1.無鎖併發 任務分段
2.CAS
3.使用最少線程 任務少,線程多,大多線程處於等待狀態
4.協程 單線程實現多任務
避免死鎖常見方法:
1.避免一個線程同時獲取多個鎖。
2.避免一個線程內在鎖內佔用多個資源,儘量保證每個鎖佔用一個
3.嘗試使用定時鎖lock.tryLock
4.對於數據庫鎖,加鎖解鎖必須在一個連接內
資源限制(將串行執行的部分變成併發執行,資源不足會變慢)
硬件:帶寬,硬盤,使用集羣 數據id%機器數
軟件:數據庫連接數,socket數 使用資源複用
根據不同的資源限制調整程序的併發度
synchronized實現同步的基礎:
1.普通同步方法,鎖實例對象
2.靜態 Class對象
3.同步方法塊,鎖括號中對象 synchronized(對象)
在jvm中使用Monitor對象來實現,方法同步(也可用以下方法實現這裏用另外
的一種方法)、
代碼塊同步(monitorenter,monitorexit)
線程通信:1.共享內存(隱式通信) 2.消息傳遞(之間發送消息,顯式)
顯式同步,需要指定互斥塊 隱式同步發送必須在接受消息前
java使用共享內存模型
在沒有 數據依賴性 的情況下,會進行指令重排
(讀寫、寫讀、寫寫)
當存在控制相關性的時候 也會進行指令重排
if(flag){
a = i*i;
}
先執行temp=i*i;(存入重排序緩衝) 然後判斷flag,爲真執行a=temp;
單線程中對控制依賴的操作重排序不會改變執行結果,多線程可能會!!
順序一致性內存模型
1.一個線程所有操作必須按照程序的順序進行
2.所有線程都能看到單一操作執行順序,每個操作原子,且立刻對其他線程可見。
JMM(java memory model)通過同步程序來達到順序一致性效果,儘可能爲編譯器和處理器優化便利。
JMM 不保證
1.單線程內操作按程序順序執行
2.所有線程都能看到一致的操作執行順序
3.對64bit的long double 寫操作的原子性 jdk5以後讀爲原子操作
volatile的內存語義
(可見性)
對被該修飾的變量,單個讀和寫可看做是使用同一個鎖對單個讀寫進行同步
如果是多個volatile操作或者複合操作(volatile++)整體上不具有原子性
(單個volatile變量的讀寫的原子性)
讀,,寫,,寫讀,讀寫不具有原子性
(部分有序性)
操作1 普通讀寫 操作2 volatile寫禁止,,其他的允許
當第二個操作是volatile寫的時候,禁止重排序
當第一個操作是volatile讀,禁止重排序
第一位volatile寫,第二爲volatile讀寫不允許排序 普通讀寫可以
-------------------------------------------------------------------------------------------
普通讀寫 volatile讀
StoreStore屏障 禁止普通寫與下一步重排 LoadLoad屏障 禁止後面的
volatile寫 LoadStore屏障 讀寫和volatile重排
StoreLoad屏障 禁止上一步與後面可能的volatile讀寫重排 普通讀寫
----------------------------------------------------------------------------------------------
LoadLoad 確保Load1數據裝載先於Load2以及所有後續裝載指令
LoadStore 確保Load1數據裝載先於Store一局所有後續存儲指令
StoreStore 確保Store1數據對其他處理器可見(刷新到內存)先於Store2以及所有後續存儲指令
StoreLoad 確保Store1數據對其他處理器可見(刷新到內存)先於Load2以及所有後續裝載指令的裝載。
鎖的內存語義 除了讓 臨界區互斥執行 釋放鎖的線程向獲取同一個鎖的線程發送消息
1.線程A釋放一個鎖,實質上是A向接下來獲取該鎖的某線程發送消息
2.線程B獲取一個鎖,實質上是B獲取了之前的某個線程發出的消息
3。A釋放,B獲取,即A通過主內存向B發送消息
AQS(AbstractQueueSynchronizer)
final域的重排序規則
以下不能重排序
1.構造函數內對一個final域的寫入,與隨後把該對象的引用賦值給一個引用變量
2.初次讀一個final域的對象的引用,與隨後初次讀這個final域
final域的寫重排序規則
1.JMM禁止編譯器把final域的寫重排到構造函數之外
2.編譯器會在final寫之後,構造函數return之前,插入一個StoreStore屏障禁止把final寫重排到構造函數之
外
final域的讀重排序規則
1.一個線程中初次讀對象引用與初次讀對該對象包含的final域,JMM禁止處理器重排序這兩個操作(僅對CPU)
會在final域讀操作之前插入一個LoadLoad 兩個操作之間存在間接依賴關係,禁止少數CPU重排序這兩個操作。
DoubleCheck和延遲初始化 減少創建類的開銷增加了耗時
DoubleCheck
利用volatile 禁止 初始化對象與設置指向instance的空間重排序
基於類初始化 允許重排序,但是不被其他線程看到
private static class InstanceHolder{
public static Resource re = new Resource(); //會使用 Class對象初始化鎖 和 state字段
} //其中狀態有noInitialization initializing
public static Resource getResource(){ //initialized 三個狀態 ,其他檢測到字段wei
return InstanceHolder.re; //第二個,就釋放鎖,掛起等待喚醒
}
Daemon線程 在start之前設置 其線程中的finally塊不一定執行
第4章
Thread.interrupted()對線程的中斷方法進行復位
安全線程終止應使用 中斷 或者 標識位
!!!僅僅通過flag 然後循環等待,會 難以保持及時性,難以 降低開銷
通過一個object的wait(會釋放鎖) notify ,以及flag 循環等待 來對兩個線程之間傳遞消息
ThreadA ThreadB 需要都獲得 object的對象鎖
notifyAll() 會把所有等待線程全移動到同步隊列 被移動的線程狀態由Waiting變爲Blocked
static boolean flag = true;
static Object lock = new Object();
ThreadA
synchronized(lock){
while(flag){
lock.wait();
}
處理邏輯
}
ThreadB
synchronized(lock){
flag = false;
lock.notifyAll();
}
thread.join() join()會在thread死亡後返回當前線程
ThreadLocal 可以用於計算方法時間 set(obj) get(obj) 以一個ThreadLocal爲鍵,任意對象爲值的存儲結構
線程等待超時模式
public synchronized Object get(long mills) throws InterruptedException{ //對當前對象加鎖
long future = System.currentTimeMills()+mills; // 等待終止時間
long remaining = mills; // 剩餘等待時間
while((result==null)&&remaining>0){ // 沒有得到結果 且 仍存在等待時間 循環等待
wait(remaining); //等待
remaining = future - System.currentTimeMills(); // 等待後 剩餘時間
}
return result; //返回結果
}