并發(fā)包下的重入鎖(ReentrantLock)

重入鎖可以完全替代synchronized關(guān)鍵字。在JDK5.0的早期版本中,重入鎖的性能完全好于synchronized。但是JDK6.0對synchronized做了大量的優(yōu)化,使得兩者差距并不大了,但是?ReentrantLock 靈活性要遠高于synchronized。

1.為什么叫重入鎖?

答:從名稱上看,翻譯挺貼切的, re-entrant-lock 重-入-鎖。之所以這么叫是因為這種鎖是可以反復(fù)進入的。當(dāng)然,僅僅局限于一個線程是可以反復(fù)的。

lock.lock();

lock.lock();

try {

i++;

}catch (Exception e) {

lock.unlock();

lock.unlock();

}

一個線程可以連續(xù)兩次獲得同一把鎖。釋放鎖也要多釋放一次。

2.中斷響應(yīng)

對于synchronized來說,如果一個線程在等待鎖,那么只有與兩種結(jié)果,第一,得到鎖,第二 繼續(xù)等待。而使用重入鎖,則提供另外一種可能,那就是線程可以被中斷。也就是程序可以選擇性的取消對鎖的請求,在有些時候還是有必要的。

舉個例子:如果你約好了和朋友一起去玩,結(jié)果等了1個小時,你朋友給你打電話說有突發(fā)情況不能去了。那么你一定掃興的打道回府了。中斷就類似于這么一套機制。如果一個線程正在等待鎖,那么它依然可以收到一個通知,被告知無須再等待,可以停止工作了。這種情況對處理死鎖是有一定幫助的。

下面的代碼產(chǎn)生了一個死鎖,我們通過重入鎖的中斷,可以有效地解決這個問題。

package com.example.test;

import java.util.concurrent.locks.ReentrantLock;

/**

* Created by sql_j on 2018/3/18.

*/

public class IntLockimplements Runnable {

public static ReentrantLocklock1 =new ReentrantLock();

public static ReentrantLocklock2 =new ReentrantLock();

int lock;

public IntLock(int lock){

this.lock = lock;

}

@Override

? ? public void run() {

try {

if(lock==1){

lock1.lockInterruptibly();

try {

Thread.sleep(1000);

}catch (InterruptedException e) {

e.printStackTrace();

}

lock2.lockInterruptibly();

System.out.println("我是線程1");

}else{

lock2.lockInterruptibly();

try {

Thread.sleep(1000);

}catch (InterruptedException e) {

e.printStackTrace();

}

lock1.lockInterruptibly();

System.out.println("我是線程2");

}

}catch (Exception e) {

e.printStackTrace();

}finally {

if(lock1.isHeldByCurrentThread()){

lock1.unlock();

}

if(lock2.isHeldByCurrentThread()){

lock2.unlock();

}

}

}

public static void main(String[] args){

IntLock intLock1 =new IntLock(1);

IntLock intLock2 =new IntLock(2);

Thread thread1 =new Thread(intLock1);

Thread thread2 =new Thread(intLock2);

thread1.start();

thread2.start();

try {

Thread.sleep(1000);

}catch (InterruptedException e) {

e.printStackTrace();

}

thread1.interrupt();

}

}

線程thread1和線程thread2啟動后, thread1先占用lock1 再占用lock2;thread2先占用lock2 再占用lock1,很容易形成 兩個線程相互等待。這里用lickInterruptibly()方法。這個方法可以在等待鎖的過程中,響應(yīng)中斷。




3.trylock()

我們可以用trylock()方法進行限時等待。



public class IntLockimplements Runnable {

public static ReentrantLocklock1 =new ReentrantLock();

@Override

? ? public void run() {

try {

try {

if(lock1.tryLock(3, TimeUnit.SECONDS)){

Thread.sleep(6000);

}else{

System.out.println("get lock feild");

}

}catch (InterruptedException e) {

e.printStackTrace();

}

}catch (Exception e) {

e.printStackTrace();

}finally {

if(lock1.isHeldByCurrentThread()){

lock1.unlock();

}

}

}

}


我們同樣開啟兩個線程,這時候 trylock(),會在3S內(nèi)試著去請求鎖,請求不到 就會返回false。


ReentrantLock.tryLock();方法也可以不帶參數(shù)直接運行。



3.公平鎖

我們在平時的應(yīng)用中就知道,在大多數(shù)情況下,鎖的申請是非公平的,無序的。這就好比,買票不要排隊,誰能擠到最前面,誰就先買到票。而公平鎖,是按照時間順序來排隊的,它不會產(chǎn)生饑餓現(xiàn)象,只要你排隊,就一定能得到資源。如果我們使用synchronized關(guān)鍵字進行鎖的控制,那它就是非公平的。


示例:


public static ReentrantLock fireLock = new ReentrantLock(true);

? ? @Override

? ? public void run() {

? ? ? ? try {

? ? ? ? ? ? try {

? ? ? ? ? ? ? ? if(fireLock.tryLock(3, TimeUnit.SECONDS)){

? ? ? ? ? ? ? ? }else{

? ? ? ? ? ? ? ? ? ? System.out.println("get lock feild");

? ? ? ? ? ? ? ? }

? ? ? ? ? ? } catch (InterruptedException e) {

? ? ? ? ? ? ? ? e.printStackTrace();

? ? ? ? ? ? }

? ? ? ? } catch (Exception e) {

? ? ? ? ? ? e.printStackTrace();

? ? ? ? } finally {

? ? ? ? ? ? if(fireLock.isHeldByCurrentThread()){

? ? ? ? ? ? ? ? fireLock.unlock();

? ? ? ? ? ? }

? ? ? ? }

? ? }




4.重入鎖的好搭檔:Condition條件

Condition和 Object.wait()、Obejct.notify() 方法的作用是大致相同的。只不過,Condition是作用在ReentrantLock上,而Object.wait()、Obejct.notify() 是作用在 synchronized上的。

await():方法會使當(dāng)前線程等待,同時釋放當(dāng)前鎖,其它線程中使用signal()或signalAll()方法時,線程會重新獲得鎖,并繼續(xù)執(zhí)行?;蛘弋?dāng)線程被中斷時,也能跳出等待。

awaitUninteerrupibly():和await()方法類似,但是不會在等待響應(yīng)過程中被中斷。

signal():用于喚醒一個在等待中的線程。

signalAll():用于喚醒所有在等待的線程。

示例:

package com.example.test;

import java.util.concurrent.TimeUnit;

import java.util.concurrent.locks.Condition;

import java.util.concurrent.locks.ReentrantLock;

/**

* Created by sql_j on 2018/3/18.

*/

public class IntLockimplements Runnable {

private static ReentrantLocklock =new ReentrantLock(true);

private static Conditioncondition =lock.newCondition();

@Override

? ? public void run() {

try {

lock.lock();

condition.await();

System.out.println("執(zhí)行");

}catch (InterruptedException e) {

e.printStackTrace();

}finally {

if(lock.isHeldByCurrentThread()){

lock.unlock();

}

}

}

public static void main(String[] args){

IntLock intLock =new IntLock();

Thread thread =new Thread(intLock);

thread.start();

try {

Thread.sleep(3000);

}catch (InterruptedException e) {

e.printStackTrace();

}

lock.lock();

condition.signal();

lock.unlock();

}

}


5.對于ReentrantLock的應(yīng)用整理如下

? ? lock():獲得鎖,如果鎖被占用,則等待。

? ? lockInterruptibly():獲得鎖,但優(yōu)先響應(yīng)中斷。

? ? tryLock():嘗試獲得鎖,如果成功,返回true,失敗返回false。該方法不等待,立即返回。

? ? tryLock(long time,TimeUnit unit):在給定時間內(nèi)嘗試獲得鎖。

? ? unlock():釋放鎖。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容