ReentrantLock
先看一段程序:
public class DemoT {
// 定義一個共享變量
private static int count = 0;
// 用來操作共享變量的方法
public static void inc() {
try {
// 爲了展示出多線程同時操作同一數據會出現問題。如果去掉之後可能會正常計算出數據
Thread.sleep(1);
count++;
// decr();
} catch (Exception e) {
e.printStackTrace();
} finally {
}
}
public static void main(String[] args) throws InterruptedException {
// 新建三個線程 每個循環100遍,去調用inc方法操作共享變量
Thread thread = new Thread(() -> {
for (int i = 0; i < 100; i++) {
DemoT.inc();
}
});
thread.start();
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 100; i++) {
DemoT.inc();
}
});
thread2.start();
Thread thread3 = new Thread(() -> {
for (int i = 0; i < 100; i++) {
DemoT.inc();
}
});
thread3.start();
// 保證三個子線程已經運行結束
Thread.sleep(2000);
// 打印共享變量
System.out.println("count:" + count);
}
}
想要的結果:(打印:count:300)
實際打印結果:count:285(可能不是285,但是基本上都是小於300)
添加重入鎖(ReentrantLock)(還是之前的代碼只不過加了一把鎖)
public class DemoT {
// 定義一個共享變量
private static int count = 0;
//添加重入鎖 @param fair {@code true} if this lock should use a fair ordering policy(譯:@param fair{@code true}如果此鎖應使用公平排序策略)
// 如果添加true 則是使用公平鎖,否則爲非公平鎖
static Lock lock = new ReentrantLock(true);
// 用來操作共享變量的方法
public static void inc() {
lock.lock(); //獲得鎖(互斥鎖)
try {
// 爲了展示出多線程同時操作同一數據會出現問題。如果去掉之後可能會正常計算出數據
Thread.sleep(1);
count++;
// decr();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();//釋放鎖
}
}
public static void main(String[] args) throws InterruptedException {
// 新建三個線程 每個循環100遍,去調用inc方法操作共享變量
Thread thread = new Thread(() -> {
for (int i = 0; i < 100; i++) {
DemoT.inc();
}
});
thread.start();
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 100; i++) {
DemoT.inc();
}
});
thread2.start();
Thread thread3 = new Thread(() -> {
for (int i = 0; i < 100; i++) {
DemoT.inc();
}
});
thread3.start();
// 保證三個子線程已經運行結束
Thread.sleep(2000);
// 打印共享變量
System.out.println("count:" + count);
}
}
運行得到:count:300
實現想要的結果,所以這是一個互斥鎖,只允許一個線程進入當前範圍
那爲什麼他叫“重入”呢?是因爲他可以獲得當前鎖的情況下,還可以進入另外一個以當前鎖爲鎖頭的代碼塊。例如下面代碼:(還是之前代碼,只不過添加了一個方法體用來對共享變量遞減)
public class DemoT {
// 定義一個共享變量
private static int count = 0;
//添加重入鎖 @param fair {@code true} if this lock should use a fair ordering policy(譯:@param fair{@code true}如果此鎖應使用公平排序策略)
// 如果添加true 則是使用公平鎖,否則爲非公平鎖
static Lock lock = new ReentrantLock(true);
// 用來操作共享變量的方法
public static void inc() {
lock.lock(); //獲得鎖(互斥鎖)
try {
// 爲了展示出多線程同時操作同一數據會出現問題。如果去掉之後可能會正常計算出數據
Thread.sleep(1);
count++;
decr();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();//釋放鎖
}
}
public static void decr() {
lock.lock(); //state=2 //ThreadA再次來搶佔鎖 : 不需要再次搶佔鎖,而是隻增加重入的次數
try {
count--;
} finally {
lock.unlock(); //state=1
}
}
public static void main(String[] args) throws InterruptedException {
// 新建三個線程 每個循環100遍,去調用inc方法操作共享變量
Thread thread = new Thread(() -> {
for (int i = 0; i < 100; i++) {
DemoT.inc();
}
});
thread.start();
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 100; i++) {
DemoT.inc();
}
});
thread2.start();
Thread thread3 = new Thread(() -> {
for (int i = 0; i < 100; i++) {
DemoT.inc();
}
});
thread3.start();
// 保證三個子線程已經運行結束
Thread.sleep(2000);
// 打印共享變量
System.out.println("count:" + count);
}
}
預計結果:count:0
實際運行結果:count:0
概念
重入是如何實現的?
內部有一個 volatile 修飾的變量,噹噹前線程添加一把鎖變量+1 ,當變量結束一個鎖,則-1;當爲0 說明當前線程無當前鎖 volatile int waitStatus;
沒有獲取到當前鎖的線程怎麼操作?以及是怎麼保存的?
會放在一個雙向鏈表 使用 addWaiter 將未獲得鎖的線程加入到隊列
並且區分公平鎖和非公平鎖
非公平鎖:如果當前線程執行到鎖的位置,直接去搶佔鎖,如果搶到,直接拿到鎖,運行。鏈表隊列中繼續等待。
公平鎖:如果當前線程執行到鎖的位置,先去鏈表中判斷是否存在其他線程,如果存在,加入隊列末尾。如果不存在,去判斷鎖是否被其他線程拿到,如果有,添加到鏈表隊列,沒有執行代碼塊。