【福利】關注微信公眾號:深夜程猿,回覆關鍵字即可獲取學習視訊、簡歷模版
歡迎讀者批評指正~~
背景知識
在Java中,要想做到執行緒同步的傳統方式是使用關鍵字synchronized。synchronized關鍵字提供了一個基本的同步機制,但是它的使用是十分嚴格的。例如,一個執行緒只能獲取一次鎖。同步的阻塞沒有提供任何阻塞佇列機制,當一個執行緒退出的時候,任何的執行緒都可以獲取鎖。這樣會導致某些執行緒在一段長時間內處於飢餓狀態(獲取不到鎖,無法訪問資源)。可重入鎖(Reentrant Lock)的引入使得解決這方面的問題變得簡便,處理Java的執行緒同步問題也會加靈活。
什麼是可重入鎖
可重入鎖實現了Lock介面,提供了一些共享資源的方法。操作共享資源的程式碼處於lock方法和unlock方法的呼叫之間。這使得當前工作執行緒獲取鎖,阻塞其他想要獲取共享資源鎖的執行緒。 正如名字那樣,可重入鎖使得執行緒可以多次進入資源鎖(就是佔有該資源的執行緒在為完全釋放資源鎖的時候,還可以再一次進入該資源鎖)。當執行緒第一次進入鎖的時候,會設定一個鎖計數值(hold count)為1,線上程未釋放鎖的時候可以再一次進入該鎖,鎖計數值遞增.對於每一次釋放鎖的請求,鎖計數值會遞減,直到鎖計數值為0的時候,資源才會真正被釋放。 可重入鎖也會提供一個公平的引數,使得鎖遵守鎖的請求順序。比如,在一個執行緒釋放資源的時候,將鎖分配給等待最長時間的執行緒。在構造可重入鎖的時候,傳入true就可以設定公平模式。 unlock方法要放在finally語句中呼叫,保證任何情況都會釋放鎖。
ReentrantLock的常用方法
lock()
呼叫lock方法,如果資源鎖未被其它執行緒佔用(當前執行緒佔用還可以再一次獲取鎖)就獲取資源鎖,鎖計數值遞增,執行緒獲得資源鎖並立即;如果資源鎖被其它執行緒佔用,那麼就進入休眠狀態,直到獲取到資源鎖,同時鎖計數值設為1;
tryLock()
呼叫tryLock方法,如果資源沒有被其它任何執行緒佔有(當前執行緒佔用還可以再一次獲取鎖)就獲取資源鎖,返回true並遞增鎖計數值;如果資源鎖被其它執行緒佔用佔用,返回false,執行緒不會退出,而是執行完run方法邏輯再嘗試獲取資源鎖,依次下去,直到獲取到資源鎖;
tryLock(long timeout, TimeUnit unit)
作用和tryLock一樣,只是為獲取到資源鎖的時候,執行緒會等待(不斷嘗試獲取資源鎖),超過設定的超時時間才會返回false;
lockInterruptibly()
呼叫這個方法,如果資源沒有被其它任何執行緒佔有(當前執行緒佔用還可以再一次獲取鎖)就獲取資源鎖,遞增鎖計數值;如果再獲取鎖的過程,當前執行緒被其它執行緒interrupt(其它執行緒呼叫當前執行緒的interrupt()方法),那麼就會要求處理InterruptedException;如果當前執行緒沒有獲取到鎖,就會休眠。休眠過程,如果其它執行緒interupt當前執行緒,也會要求處理InterruptedException
unlock()
呼叫unlock方法會對鎖計數值遞減,只有鎖計數值的值是0的時候才會釋放資源
演示demo
public class Test {
static class Worker implements Runnable {
String name;
ReentrantLock re;
public Worker(ReentrantLock rl, String n) {
re = rl;
name = n;
}
public void run() {
boolean done = false;
while (!done) {
//獲取外部鎖
System.out.println("task name - " + name + " try to acquired lock by tryLock()");
boolean ans = re.tryLock();
if (ans) {
// 獲取到鎖
try {
Date d = new Date();
SimpleDateFormat ft = new SimpleDateFormat("hh:mm:ss");
System.out.println("task name - " + name
+ " outer lock acquired at "
+ ft.format(d)
+ " Doing outer work");
Thread.sleep(1500);
// 獲取內部鎖
System.out.println("task name - " + name + " try to acquired lock by lock()");
re.lock();
try {
d = new Date();
ft = new SimpleDateFormat("hh:mm:ss");
System.out.println("task name - " + name
+ " inner lock acquired at "
+ ft.format(d)
+ " Doing inner work");
System.out.println("Lock Hold Count - " + re.getHoldCount());
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//釋放內部鎖
System.out.println("task name - " + name +
" releasing inner lock");
re.unlock();
}
System.out.println("Lock Hold Count - " + re.getHoldCount());
System.out.println("task name - " + name + " work done");
done = true;
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//釋放外部鎖
System.out.println("task name - " + name +
" releasing outer lock");
re.unlock();
System.out.println("Lock Hold Count - " +
re.getHoldCount());
}
} else {
// 沒有獲取到鎖
System.out.println("task name - " + name +
" waiting for lock");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
static final int MAX_T = 2;
public static void main(String[] args) {
ReentrantLock rel = new ReentrantLock();
ExecutorService pool = Executors.newFixedThreadPool(MAX_T);
Runnable w1 = new Worker(rel, "Job1");
Runnable w2 = new Worker(rel, "Job2");
Runnable w3 = new Worker(rel, "Job3");
Runnable w4 = new Worker(rel, "Job4");
pool.execute(w1);
pool.execute(w2);
pool.execute(w3);
pool.execute(w4);
pool.shutdown();
}
}
複製程式碼
輸出
task name - Job1 try to acquired lock by tryLock()
task name - Job2 try to acquired lock by tryLock()
task name - Job2 waiting for lock
task name - Job1 outer lock acquired at 05:29:50 Doing outer work
task name - Job2 try to acquired lock by tryLock()
task name - Job2 waiting for lock
task name - Job1 try to acquired lock by lock()
task name - Job1 inner lock acquired at 05:29:51 Doing inner work
Lock Hold Count - 2
task name - Job2 try to acquired lock by tryLock()
task name - Job2 waiting for lock
task name - Job2 try to acquired lock by tryLock()
task name - Job2 waiting for lock
task name - Job1 releasing inner lock
Lock Hold Count - 1
task name - Job1 work done
task name - Job1 releasing outer lock
Lock Hold Count - 0
task name - Job3 try to acquired lock by tryLock()
task name - Job3 outer lock acquired at 05:29:53 Doing outer work
task name - Job2 try to acquired lock by tryLock()
task name - Job2 waiting for lock
task name - Job3 try to acquired lock by lock()
task name - Job3 inner lock acquired at 05:29:54 Doing inner work
Lock Hold Count - 2
task name - Job2 try to acquired lock by tryLock()
task name - Job2 waiting for lock
task name - Job2 try to acquired lock by tryLock()
task name - Job2 waiting for lock
task name - Job3 releasing inner lock
Lock Hold Count - 1
task name - Job3 work done
task name - Job3 releasing outer lock
Lock Hold Count - 0
task name - Job4 try to acquired lock by tryLock()
task name - Job4 outer lock acquired at 05:29:56 Doing outer work
task name - Job2 try to acquired lock by tryLock()
task name - Job2 waiting for lock
task name - Job4 try to acquired lock by lock()
task name - Job4 inner lock acquired at 05:29:57 Doing inner work
Lock Hold Count - 2
task name - Job2 try to acquired lock by tryLock()
task name - Job2 waiting for lock
task name - Job2 try to acquired lock by tryLock()
task name - Job2 waiting for lock
task name - Job4 releasing inner lock
Lock Hold Count - 1
task name - Job4 work done
task name - Job4 releasing outer lock
Lock Hold Count - 0
task name - Job2 try to acquired lock by tryLock()
task name - Job2 outer lock acquired at 05:30:00 Doing outer work
task name - Job2 try to acquired lock by lock()
task name - Job2 inner lock acquired at 05:30:01 Doing inner work
Lock Hold Count - 2
task name - Job2 releasing inner lock
Lock Hold Count - 1
task name - Job2 work done
task name - Job2 releasing outer lock
Lock Hold Count - 0
複製程式碼