重入鎖可以完全替代synchronized關(guān)鍵字吹散。在JDK5.0的早期版本中,重入鎖的性能完全好于synchronized申窘。但是JDK6.0對synchronized做了大量的優(yōu)化迅箩,使得兩者差距并不大了,但是?ReentrantLock 靈活性要遠(yuǎn)高于synchronized屋摇。
1.為什么叫重入鎖揩魂?
答:從名稱上看,翻譯挺貼切的, re-entrant-lock 重-入-鎖炮温。之所以這么叫是因?yàn)檫@種鎖是可以反復(fù)進(jìn)入的火脉。當(dāng)然,僅僅局限于一個(gè)線程是可以反復(fù)的柒啤。
lock.lock();
lock.lock();
try {
i++;
}catch (Exception e) {
lock.unlock();
lock.unlock();
}
一個(gè)線程可以連續(xù)兩次獲得同一把鎖倦挂。釋放鎖也要多釋放一次。
2.中斷響應(yīng)
對于synchronized來說担巩,如果一個(gè)線程在等待鎖方援,那么只有與兩種結(jié)果,第一涛癌,得到鎖犯戏,第二 繼續(xù)等待。而使用重入鎖拳话,則提供另外一種可能先匪,那就是線程可以被中斷。也就是程序可以選擇性的取消對鎖的請求弃衍,在有些時(shí)候還是有必要的呀非。
舉個(gè)例子:如果你約好了和朋友一起去玩,結(jié)果等了1個(gè)小時(shí),你朋友給你打電話說有突發(fā)情況不能去了岸裙。那么你一定掃興的打道回府了猖败。中斷就類似于這么一套機(jī)制。如果一個(gè)線程正在等待鎖降允,那么它依然可以收到一個(gè)通知恩闻,被告知無須再等待,可以停止工作了拟糕。這種情況對處理死鎖是有一定幫助的判呕。
下面的代碼產(chǎn)生了一個(gè)死鎖,我們通過重入鎖的中斷送滞,可以有效地解決這個(gè)問題侠草。
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啟動(dòng)后, thread1先占用lock1 再占用lock2犁嗅;thread2先占用lock2 再占用lock1边涕,很容易形成 兩個(gè)線程相互等待。這里用lickInterruptibly()方法褂微。這個(gè)方法可以在等待鎖的過程中功蜓,響應(yīng)中斷。
3.trylock()
我們可以用trylock()方法進(jìn)行限時(shí)等待宠蚂。
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();
}
}
}
}
我們同樣開啟兩個(gè)線程式撼,這時(shí)候 trylock(),會(huì)在3S內(nèi)試著去請求鎖求厕,請求不到 就會(huì)返回false著隆。
ReentrantLock.tryLock();方法也可以不帶參數(shù)直接運(yùn)行。
3.公平鎖
我們在平時(shí)的應(yīng)用中就知道呀癣,在大多數(shù)情況下美浦,鎖的申請是非公平的,無序的项栏。這就好比浦辨,買票不要排隊(duì),誰能擠到最前面沼沈,誰就先買到票流酬。而公平鎖,是按照時(shí)間順序來排隊(duì)的列另,它不會(huì)產(chǎn)生饑餓現(xiàn)象康吵,只要你排隊(duì),就一定能得到資源访递。如果我們使用synchronized關(guān)鍵字進(jì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():方法會(huì)使當(dāng)前線程等待踪古,同時(shí)釋放當(dāng)前鎖含长,其它線程中使用signal()或signalAll()方法時(shí),線程會(huì)重新獲得鎖伏穆,并繼續(xù)執(zhí)行拘泞。或者當(dāng)線程被中斷時(shí)枕扫,也能跳出等待陪腌。
awaitUninteerrupibly():和await()方法類似,但是不會(huì)在等待響應(yīng)過程中被中斷烟瞧。
signal():用于喚醒一個(gè)在等待中的線程诗鸭。
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):在給定時(shí)間內(nèi)嘗試獲得鎖。
? ? unlock():釋放鎖脆诉。