1.Lock接口的三個實作類
- ReentrantLock(可重入鎖)
- ReadLock(讀鎖)
- WriteLock(寫鎖)
2.自定義互斥鎖實作
- 通過實作Lock接口的lock()方法和unlock()方法來完成自定義實作
- ps:主要思想就是設定一個狀态量,根據狀态量來判斷是否鎖被占用,如果被占用就wait(),如果沒被占用就使用。
public class MyLock implements Lock {
/**
* 是否擁有鎖
*/
private volatile Boolean isHoldLock = false;
/**
* 同一時刻,有且僅有一個線程擁有鎖,
* 其他線程隻有等待擁有鎖的線程釋放鎖才能擷取鎖
*/
@Override
public synchronized void lock() {
// 通過自旋的方式去判斷是否擁有鎖,沒有鎖就進入線程等待
while (isHoldLock) {
try {
wait ();
} catch (InterruptedException e) {
e.printStackTrace ();
}
}
// 設定線程擁有鎖
isHoldLock = true;
}
/**
* 釋放鎖
*/
@Override
public synchronized void unlock() {
// 随機喚醒一個等待的線程
notify ();
// 将該持有鎖釋放
isHoldLock = false;
}
// 忽略以下重寫代碼
}
public class LockDemo {
private static Lock lock = new MyLock ();
private static CountDownLatch countDownLatch = new CountDownLatch (10);
private static int num = 0;
public static void inCreate() {
// 擷取鎖
lock.lock ();
num++;
// 釋放鎖
lock.unlock ();
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread (() -> {
for (int j = 0; j < 1000000; j++) {
inCreate ();
}
// 線程執行結束執行countDown,對計數減一
countDownLatch.countDown ();
}).start ();
}
while (true) {
// 所有線程執行完畢,輸出num
if (countDownLatch.getCount () == 0) {
System.out.println (num);
break;
}
}
}
}
控制台輸出:
3.自定義重入鎖實作
- ps:可重入鎖的實作就是在互斥鎖的基礎上加上線程判斷,并且使用一個reentryCount來記錄鎖的層數,以便于釋放鎖時的判斷。
public class MyLock implements Lock {
/**
* 是否擁有鎖
*/
private volatile Boolean isHoldLock = false;
/**
* 目前擁有鎖的線程
*/
private Thread holdLockThread = null;
/**
* 重入計數
*/
private int reentryCount = 0;
/**
* 同一時刻,有且僅有一個線程擁有鎖,
* 其他線程隻有等待擁有鎖的線程釋放鎖才能擷取鎖
*/
@Override
public synchronized void lock() {
// 通過自旋的方式去判斷是否擁有鎖,并且是否被目前線程持有,否則就進入線程等待
while (isHoldLock && Thread.currentThread () != holdLockThread) {
try {
wait ();
} catch (InterruptedException e) {
e.printStackTrace ();
}
}
// 設定線程擁有鎖
isHoldLock = true;
// 給目前線程指派給holdLockThread
holdLockThread = Thread.currentThread ();
// 重入次數自增
reentryCount++;
}
/**
* 釋放鎖
*/
@Override
public synchronized void unlock() {
// 判斷目前線程是否是持有鎖的線程,是,重入次數減去1,不是就不做處理
if (holdLockThread == Thread.currentThread ()) {
reentryCount--;
if (reentryCount == 0) {
// 随機喚醒一個等待的線程
notify ();
// 将該持有鎖釋放
isHoldLock = false;
}
}
}
// 忽略其他重寫方法
}
public class ReentryDemo {
private Lock lock = new MyLock ();
public void methodA(){
lock.lock ();
System.out.println (String.format ("線程%s進入方法A",Thread.currentThread ().getName ()));
methodB ();
lock.unlock ();
}
public void methodB(){
lock.lock ();
System.out.println (String.format ("線程%s進入方法B",Thread.currentThread ().getName ()));
lock.unlock ();
}
public static void main(String[] args) {
new ReentryDemo ().methodA ();
}
}
控制台輸出: ps:可重入就是說某個線程已經獲得某個鎖,可以再次擷取鎖而不會出現死鎖,叫可重入鎖
4.總結
- ps:通過實作JDK提供的Lock接口,來自定義鎖的實作,可以根據自己的業務要求編寫,一般情況下使用ReentrantLock就可以了
- 互斥鎖:主要思想就是設定一個狀态量,根據狀态量來判斷是否鎖被占用,如果被占用就wait(),如果沒被占用就使用
- 重入鎖:可重入鎖的實作就是在互斥鎖的基礎上加上線程判斷,并且使用一個reentryCount來記錄鎖的層數,以便于釋放鎖時的判斷。
連結:java多線程:ReentrantLock的使用,實作共享變量線程安全