1 減少鎖持有時間
public synchronized void syncMethod(){
othercode1();
mutextMethod();
othercode2();
}
像上述代碼這樣,在進入方法前就要得到鎖,其他線程就要在外面等待。這裏優化的一點在於,要減少其他線程等待的時間,所以,只用在有線程安全要求的程序上加鎖
public void syncMethod(){
othercode1();
synchronized(this){
mutextMethod();
}
othercode2();
}
2 減小鎖粒度將大對象(這個對象可能會被很多線程訪問),拆成小對象,大大增加並行度,降低鎖競爭。降低了鎖的競爭,偏向鎖,輕量級鎖成功率纔會提高。
最最典型的減小鎖粒度的案例就是ConcurrentHashMap。
3 鎖分離
最常見的鎖分離就是讀寫鎖ReadWriteLock,根據功能進行分離成讀鎖和寫鎖,這樣讀讀不互斥,讀寫互斥,寫寫互斥,即保證了線程安全,又提高了性能。
讀寫分離思想可以延伸,只要操作互不影響,鎖就可以分離。
比如LinkedBlockingQueue 詳情查看 《併發編程》--17.BlockingQueue解析
4 鎖粗化
通常情況下,爲了保證多線程間的有效併發,會要求每個線程持有鎖的時間儘量短,即在使用完公共資源後,應該立即釋放鎖。只有這樣,等待在這個鎖上的其他線程才能儘早的獲得資源執行任務。但是,凡事都有一個度,如果對同一個鎖不停的進行請求、同步和釋放,其本身也會消耗系統寶貴的資源,反而不利於性能的優化 。
舉個例子:
public void demoMethod(){
synchronized(lock){
//do sth.
}
//做其他不需要的同步的工作,但能很快執行完畢
synchronized(lock){
//do sth.
}
}
這種情況,根據鎖粗化的思想,應該合併public void demoMethod(){
//整合成一次鎖請求
synchronized(lock){
//do sth.
//做其他不需要的同步的工作,但能很快執行完畢
}
}
public void demoMethod(){
//整合成一次鎖請求
synchronized(lock){
//do sth.
//做其他不需要的同步的工作,但能很快執行完畢
}
}
當然這是有前提的,前提就是中間的那些不需要同步的工作是很快執行完成的。再舉一個極端的例子:
for(int i=0;i<CIRCLE;i++){
synchronized(lock){
}
}
for(int i=0;i<CIRCLE;i++){
synchronized(lock){
}
}
//在一個循環內不同得獲得鎖。雖然JDK內部會對這個代碼做些優化,但是還不如直接寫成
synchronized(lock){
for(int i=0;i<CIRCLE;i++){
}
}
synchronized(lock){
for(int i=0;i<CIRCLE;i++){
}
}
當然如果有需求說,這樣的循環太久,需要給其他線程不要等待太久,那隻能寫成上面那種。如果沒有這樣類似的需求,還是直接寫成下面
那種比較好。