在第一篇中,我整理了多線程的創建及調度多線程的方法,有興趣可以通過下面的鏈接過去瞧瞧。
https://blog.csdn.net/m0_37605407/article/details/86563800
下面正文開始。
在單線程中,由於代碼是順序運行的,同一個資源在某一個時刻只會被一個實體使用,並不會出現兩個實體同時使用同一個資源的情況。但是在多線程的情況,這種情況就變得常見了,同一個資源可能會被多個實體同時使用。此時就是出現資源不同步的情況,和一些意想不到的問題的出現。
爲了避免這種情況出現,我們可以在讀寫數據的代碼前面加上一條鎖語句避免這種情況的,這就使得在一段時間內只有一個線程可以運行這塊代碼塊(即只有一個線程可以持有這塊代碼的鎖,其他線程必須等待直到鎖被釋放,才能進行競爭從而獲得鎖)。這種效果稱之爲互斥量(mutex)。
一、使用synchronized(也叫內建鎖)進行修飾,主要分爲以下幾種場景
1、修飾方法
synchronized void fun();
注意:所有的對象都自動含有單一的鎖(也稱爲監視器),當A線程調用synchronized修飾的方法的這段時間內,其它線程調用該方法時,會被阻塞,直到A線程運行完該方法後,其他線程才能進入該方法。當有多個synchronized修飾的方法時,A線程調用其中一個synchronized的方法,其它線程在調用到其中一個synchronized修飾的方法時均會被阻塞,直至A線程運行完所調用的synchronized方法後,纔可以進去同樣是synchronized修飾的方法,只是因爲所有synchronized方法共享一個對象鎖,當然,以上說法都是針對同一個對象而言的。
2、修飾代碼塊
synchronized{
/*代碼塊*/
}
這種使用場景是在一個方法內只有少量的代碼塊需要加鎖的情況下,這種的使用效果和用synchronized修飾方法的效果一樣,可以僅將需要加鎖代碼塊進行加鎖,避免不必要的加鎖,這可以很大的提高併發的速度。
這是另外一種使用形式:
synchronized(Object object){/*代碼塊*/}
兩者的不同之處是,第一種使用形式是使用當前對象含有的鎖進行代碼加鎖,第二種使用形式是使用object對象含有的鎖進行加鎖。
注意:同一個線程可以多次獲得對象鎖。JVM負責跟蹤對象被加鎖的次數,如果一個對象被解鎖(即是所有的鎖被釋放),其計數就會變爲0,其它線程就可以來競爭鎖。只有首先獲得鎖的時候,才允許繼續獲得鎖。
3、修飾靜態方法
synchronized static void fun();
針對每個類,也有一個鎖,所有synchronized static方法可以在類的範圍內防止對static數據的併發訪問。
二、使用java.util.concurrent類庫中Lock對象進行顯式加鎖
與synchronized相比而言,Lock對象必須顯式的創建、加鎖和釋放。代碼會比較缺乏優雅,但是使用更加的靈活。
Lock lock = new ReentrantLock();
lock.lock();
try{
/*同步代碼塊*/
}finally{
lock.unlock();
}
使用時必須要嵌套try-finally使用,因爲,必須保證不管同步代碼塊出現什麼問題,到最後都必須釋放Lock對象,避免死鎖的出現。
還有,爲什麼說Lock更加靈活呢?因爲使用Lock對象進行加鎖,它可以嘗試獲取鎖且最終獲取鎖可以失敗,這是synchronized所體驗不到的操作。
Lock lock = new ReentrantLock();
if(lock.trylock()){
// 獲取鎖成功
try{
/*同步代碼塊*/
}finally{
lock.unlock();
}
}else{
log.e(TAG,"獲取鎖失敗");
}
這樣,當請求鎖時,如果鎖被其他線程所持有着,就可以選擇是否繼續等待,相對較synchronized修飾時,只能一直被阻塞,是不是就變得更加靈活了。
此外,還能設置等待阻塞的時間(即等待鎖被釋放):
Lock lock = new ReentrantLock();
try{
boolean isGetLock = lock.trylock(1,TimeUnit.SECONDS);
}catch(InterruptedException e){
}
if(isGetLock){
try{
/*同步代碼塊*/
}finally{
lock.unlock()
}
}else{
// 等待鎖超時,最終沒有能獲得鎖
}
這個在代碼出現的InterruptedException異常,後面會開一篇出來說它(其實是太晚不想寫了,嘻嘻)。
注意:使用Lock對象必須注意處理Lock對象的釋放,不然就會出現死鎖,所以在可以使用synchronized解決的情況下,就不要爲了玩騷的,去使用Lock對象,可以避免出現忘記釋放鎖,從而出現死鎖。而且使用synchronized更優雅不是嗎,哈哈哈。
到這裏,這篇就結束了,總結來說,這篇整理的互斥鎖的基本使用。