Java 8 鎖機制

主要對Java 8 經常使用的鎖如何使用進行分享

 

1、synchronized

 

(一)、用法:java

1.synchronized能夠用在方法(包含靜態方法),api

2.synchronized塊數據結構

void increment() {
  synchronized (this) {
    count += 1;
  }
}併發

 

(二)、原理:app

關鍵詞:monitor高併發

1.synchronized修飾方法性能

synchronized void increment() {
  count += 1;
}this

反編譯後會在方法的flag上帶ACC_SYNCHRONIZED標記,當方法調用時,調用指令將會檢查方法的 ACC_SYNCHRONIZED 訪問標誌是否被設置,若是設置了,執行線程將先獲取monitor,獲取成功以後才能執行方法體,方法執行完後再釋放monitor。在方法執行期間,其餘任何線程都沒法再得到同一個monitor對象.url

 

2.synchronized修飾對象spa

void increment() {
  synchronized (this) {
    count += 1;
  }
}

反編譯結果:

...

monitorenter

方法內邏輯(count += 1;)

monitorexit

...

 

monitorenter:

每一個對象有一個monitor,當monitor被佔用時就會處於鎖定狀態,線程執行monitorenter指令時嘗試獲取monitor的全部權,過程以下:
1.若是monitor的進入數爲0,則該線程進入monitor,而後將進入數設置爲1,該線程即爲monitor的全部者.
2.若是線程已經佔有該monitor,只是從新進入,則進入monitor的進入數加1.
3.若是其餘線程已經佔用了monitor,則該線程進入阻塞狀態,直到monitor的進入數爲0,再從新嘗試獲取monitor的全部權.

 

monitorexit:

執行monitorexit的線程必須是objectref所對應的monitor的全部者.
指令執行時,monitor的進入數減1,若是減1後進入數爲0,那線程退出monitor,再也不是這個monitor的全部者。其餘被這個monitor阻塞的線程能夠嘗試去獲取這個monitor的全部權.

2、Lock

 

(一)、與synchronized的區別

1.與synchronized不一樣的是,Lock徹底用Java寫成,在java這個層面是無關JVM實現的.Lock提供更靈活的鎖機制,但由於lock是經過代碼實現的,要保證鎖定必定會被釋放,就必須將unLock()放到finally{}.

 

(二)、ReentrantLock(可重入互斥鎖)

1.ReentrantLock是一個可重入的互斥鎖,重入鎖是一種遞歸無阻塞的同步機制.
2.ReentrantLock由最近成功獲取鎖,尚未釋放的線程所擁有,當鎖被另外一個線程擁有時,調用lock的線程能夠成功獲取鎖。若是鎖已經被當前線程擁有,當前線程會當即返回.

代碼示例:

ReentrantLock lock = new ReentrantLock();
if (lock.tryLock()) {//若是已經被lock,則當即返回false不會等待,達到忽略操做的效果
  try {
    //操做
  } finally {
    lock.unlock();
  }
}

================

ReentrantLock lock = new ReentrantLock(true); //公平鎖

lock.lock(); //若是被其它資源鎖定,會在此等待鎖釋放,達到暫停的效果
try {
  //操做
} finally {
  lock.unlock();
}

================

ReentrantLock lock = new ReentrantLock(true); //公平鎖 
try {
  if (lock.tryLock(5, TimeUnit.SECONDS)) {//若是已經被lock,嘗試等待5s,看是否能夠得到鎖,若是5s後仍然沒法得到鎖則返回false繼續執行 
    try {
      //操做 
    } finally {
      lock.unlock();
    }
  }
} catch (InterruptedException e) {//當前線程被中斷時(interrupt),會拋InterruptedException 
}

 

(三)、ReentrantReadWriteLock(可重入讀寫鎖, 派生自ReadWriteLock, 單獨實現, 和ReentrantLock不要緊)

1.主要特性:

(1).重入方面其內部的WriteLock能夠獲取ReadLock,可是反過來ReadLock想要得到WriteLock則永遠都不要想.

(2).WriteLock能夠降級爲ReadLock,順序是:先得到WriteLock再得到ReadLock,而後釋放WriteLock,這時候線程將保持ReadLock的持有。反過來ReadLock想要升級爲WriteLock則不可能.

(3).ReadLock能夠被多個線程持有而且在做用時排斥任何的WriteLock,而WriteLock則是徹底的互斥。這一特性最爲重要,由於對於高讀取頻率而相對較低寫入的數據結構,使用此類鎖同步機制則能夠提升併發量.

代碼示例(同一個線程中,在沒有釋放讀鎖的狀況下,就去申請寫鎖,這屬於鎖升級,ReentrantReadWriteLock是不支持):

w.lock();//先得到寫鎖
try {
  r.lock();//再得到讀鎖
  try {
    // do something
  } finally {
    r.unlock();
  }
} finally {
  w.unlock();
}

2.鎖降級

鎖順序:

rwl.writeLock().lock();//先拿寫鎖
rwl.readLock().lock();//再拿讀鎖
rwl.writeLock().unlock();
rwl.readLock().unlock();

結論:

ReentrantReadWriteLock有鎖降級機制(由於讀鎖是能夠被多個線程共享的),若是一個線程拿到了寫鎖,那麼它還能夠繼續拿到讀鎖.

 

(四)、StampedLock(時間戳鎖)

1.它是java 8在java.util.concurrent.locks新增的一個API.

2.代碼示例:

3.解釋:

所謂的樂觀讀模式,也就是若讀的操做不少,寫的操做不多的狀況下,你能夠樂觀地認爲,寫入與讀取同時發生概率不多,所以不悲觀地使用徹底的讀取鎖定,程序能夠查看讀取資料以後,是否遭到寫入執行的變動,再採起後續的措施,這一個小小改進,可大幅度提升程序的吞吐量.

4.和ReentrantReadWriteLock相比:

(1).一個線程狀況下,讀速度其4倍左右,寫是1倍.
(2).六個線程狀況下,讀性能是其幾十倍,寫性能也是近10倍左右.

 

(五)、信號量

1.鎖和信號量的區別:

鎖是用於資源或者變量的互斥訪問,而信號量是用來作"准入許可"

2.代碼示例:

相關文章
相關標籤/搜索