java的轻量锁,jvm第7节-锁(偏向锁,轻量锁,自旋锁)
在介紹鎖之前我們先介紹一個線程不安全的例子,一個全局的list,開2個線程往里面插入數(shù)據(jù),代碼如下:
package com.jvm.day6.lock.demo;
import java.util.ArrayList;
import java.util.List;
/**
* 測試都線程共享一個變量帶來的現(xiàn)象
* @Author:xuehan
* @Date:2016年3月20日下午3:35:29
*/
class NumberAdd implements Runnable{
public static List numberList =new ArrayList();
public static int startNum;
public NumberAdd(int startNum){
this.startNum = startNum;
}
@Override
public void run() {
int count = 0;
while(count < 1000000){
numberList.add(count ++ );
startNum = startNum + 2;
}
}
}
public class Test{
public static void main(String[] args) throws Exception {
Thread t1 = new Thread(new NumberAdd(1));
Thread t2 = new Thread(new NumberAdd(0));
t1.start();
t2.start();
while(t1.isAlive() || t2.isAlive()){Thread.sleep(2);}
System.out.println("集合大小" + NumberAdd.numberList.size() );
System.out.println( "最后一個值的大" + NumberAdd.numberList.get(NumberAdd.numberList.size() - 1));
for(int i = NumberAdd.numberList.size() - 10 ; i < NumberAdd.numberList.size() -1; i ++){
System.out.println(NumberAdd.numberList.get(i));
}
}
}
按照開始想的,集合里面應(yīng)該有200萬個數(shù)據(jù)了,結(jié)果卻出現(xiàn)了數(shù)組越界的錯誤,為什么呢,這是因為ArrayList不是線程安全的,用來存放數(shù)據(jù)的elementData是共享的, 線程A往list里添加數(shù)據(jù)的時候剛驗證大小通過,還沒有插入,然后輪到線程B執(zhí)行,線程B剛好插入了list該擴容的最后一個元素,然后list滿了,線程A執(zhí)行,A線程往集合里面插入元素,引起了數(shù)據(jù)越界。
jvm鎖
每個對象都一個mark頭,他的作用是:
Mark Word,對象頭的標(biāo)記,32位
描述對象的hash、鎖信息,垃圾回收標(biāo)記,年齡
指向鎖記錄的指針
指向monitor的指針
GC標(biāo)記
偏向鎖線程ID
偏向鎖
jvm控制,可以設(shè)置jvm啟動參數(shù)
大部分情況是沒有競爭的,所以可以通過偏向來提高性能
所謂的偏向,就是偏心,即鎖會偏向于當(dāng)前已經(jīng)占有鎖的線程
將對象頭Mark的標(biāo)記設(shè)置為偏向,并將線程ID寫入對象頭Mark
只要沒有競爭,獲得偏向鎖的線程,在將來進(jìn)入同步塊,不需要做同步
當(dāng)其他線程請求相同的鎖時,偏向模式結(jié)束
-XX:+UseBiasedLocking -jdk6需要手動打開
默認(rèn)啟用
在競爭激烈的場合,偏向鎖會增加系統(tǒng)負(fù)擔(dān)
偏向鎖是系統(tǒng)自帶的設(shè)置參數(shù)開啟,java代碼如下:
package com.jvm.day6.lock;
import java.util.List;
import java.util.Vector;
/**
*使用 偏向鎖-XX:+UseBiasedLocking -XX:BiasedLockingStartupDelay=0
* 不使用偏向鎖-XX:-UseBiasedLocking
* 根據(jù)測試下面代碼使用偏向鎖可提高150毫秒執(zhí)行時間 150/2300,提高效率6%
* @Author:xuehan
* @Date:2016年3月19日下午12:06:15
*/
public class DeflectionLock {
public static List numberList =new Vector();
public static void main(String[] args) throws InterruptedException {
System.out.println((int)'l');
long begin=System.currentTimeMillis();
int count=0;
int startnum=0;
while(count<10000000){
numberList.add(startnum);
startnum+=2;
count++;
}
long end=System.currentTimeMillis();
System.out.println(end-begin);
}
} ?根據(jù)我的寫實偏向鎖可以提高性能6%
輕量級鎖
BasicObjectLock,這個鎖是嵌入到線程棧中的,他有兩部組成
BasicLock和
ptr to obj hold the lock,
BasicLock里面存放著對象頭,
ptr to obj hold the lock為指向持有對象鎖的指針
普通的鎖處理性能不夠理想,輕量級鎖是一種快速的鎖定方法。
如果對象沒有被鎖定
將對象頭的Mark指針保存到鎖對象中
將對象頭設(shè)置為指向鎖的指針(在線程棧空間中)
如果輕量級鎖失敗,表示存在競爭,升級為重量級鎖(常規(guī)鎖)
在沒有鎖競爭的前提下,減少傳統(tǒng)鎖使用OS互斥量產(chǎn)生的性能損耗
在競爭激烈時,輕量級鎖會多做很多額外操作,導(dǎo)致性能下降
自旋鎖
當(dāng)競爭存在時,如果線程可以很快獲得鎖,那么可以不在OS層掛起線程,讓線程做幾個空操作(自旋)
JDK1.6中-XX:+UseSpinning開啟
JDK1.7中,去掉此參數(shù),改為內(nèi)置實現(xiàn)
如果同步塊很長,自旋失敗,會降低系統(tǒng)性能
如果同步塊很短,自旋成功,節(jié)省線程掛起切換時間,提升系統(tǒng)性能
上面的一些鎖不是Java語言層面的鎖優(yōu)化方法
他們是內(nèi)置于JVM中的獲取鎖的優(yōu)化方法和獲取鎖的步驟
偏向鎖可用會先嘗試偏向鎖
輕量級鎖可用會先嘗試輕量級鎖
以上都失敗,嘗試自旋鎖
再失敗,嘗試普通鎖,使用OS互斥量在操作系統(tǒng)層掛起
我們可以從以下方面對鎖進(jìn)行優(yōu)化
減少鎖的時間
沒必須放在同步塊的代碼盡量不要放在代碼塊里
減少鎖的粒度
將大對象,拆成小對象,大大增加并行度,降低鎖競爭
偏向鎖,輕量級鎖成功率提高
實現(xiàn)的例子如ConcurrentHashMap
使用若干個Segment :Segment[] segments
Segment中維護HashEntry
put操作時
先定位到Segment,鎖定一個Segment,執(zhí)行put
在減小鎖粒度后, ConcurrentHashMap允許若干個線程同時進(jìn)入
鎖分離
根據(jù)功能進(jìn)行鎖分離
ReadWriteLock
讀多寫少的情況,可以提高性能
讀寫分離思想可以延伸,只要操作互不影響,鎖就可以分離
LinkedBlockingQueue
隊列
鏈表
鎖粗化
通常情況下,為了保證多線程間的有效并發(fā),會要求每個線程持有鎖的時間盡量短,即在使用完公共資源后,應(yīng)該立即釋放鎖。只有這樣,等待在這個鎖上的其他線程才能盡早的獲得資源執(zhí)行任務(wù)。但是,凡事都有一個度,如果對同一個鎖不停的進(jìn)行請求、同步和釋放,其本身也會消耗系統(tǒng)寶貴的資源,反而不利于性能的優(yōu)化
for(int i=0;i<1000;i++){
synchronized(lock){}
}
// 應(yīng)該寫成
synchronized(lock){
for(int i =0; i < 1000; i ++){}
}
這時候我們要增加鎖的持有時間不要讓請求和釋放鎖頻繁的發(fā)生
鎖消除
在java方法體里如果不是共享的變量不需要同步操作的,這時候jvm會自動的優(yōu)化把鎖去掉,如StingBuffer和Vector,使用鎖消除
-server -XX:+DoEscapeAnalysis -XX:+EliminateLocks
關(guān)閉鎖消除
-server -XX:+DoEscapeAnalysis -XX:-EliminateLocks
無鎖
鎖是悲觀的操作
無鎖是樂觀的操作
無鎖的一種實現(xiàn)方式
CAS(Compare And Swap),CAS是原子的
非阻塞的同步
CAS(V,E,N)
在應(yīng)用層面判斷多線程的干擾,如果有干擾,則通知線程重試,一般這樣做會讓程序變的復(fù)雜,但性能更加好。
總結(jié)
以上是生活随笔為你收集整理的java的轻量锁,jvm第7节-锁(偏向锁,轻量锁,自旋锁)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: php中延迟绑定,PHP静态延迟绑定
 - 下一篇: java的全栈,Java全栈工程师