什么是 “可重入”?可重入就是说某个线程已经获得某个锁,可以再次获取锁而不会出现死锁。即可重入锁的作用就是为了避免死锁,java中synchronized和ReentrantLock都是可重入锁。
//synchronized 可重入锁
private void test() {
// 第一次获得锁
synchronized (this) {
while (true) {
// 第二次获得同样的锁
synchronized (this) {
System.out.println("ReentrantLock!");
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//ReentrantLock可重入锁
Lock lock = new ReentrantLock();
private void test1() {
lock.lock();
try {
test2();
} finally {
lock.unlock();
}
}
private void test2() {
lock.lock();
try {
System.out.println("ReentrantLock!");
} finally {
lock.unlock();
}
}
一.自定义不可重入锁
所谓不可重入锁,即若当前线程执行某个方法已经获取了该锁,那么在方法中尝试再次获取锁时,就会获取不到被阻塞。下面的线程执行test1()方法首先获取lock,接下来执行test2()方法就无法执行test2()中的逻辑,必须先释放锁。
// 不可重入锁
class Lock {
//是否占用
private boolean isLocked = false;
//使用锁
public synchronized void lock() {
while (isLocked) {//已经占用
try {
this.wait();//等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
isLocked = true;//修改标识为已经占用
}
//释放锁
public synchronized void unLock() {
isLocked = false;//修改标识为未占用
notify();//唤醒等待线程
}
}
============使用===========
Lock lock = new Lock();
public void test1() {
lock.lock();
test2();
lock.unLock();
}
private void test2() {
lock.lock();
//...
lock.unLock();
}
二.自定义可重入锁
流程:
- 定义锁占用标识、存储线程、线程锁持有数量;
- 使用锁:判断是否已经占用和当前线程是否不等于存储线程,如果条件符合进入等待,不符合则修改占用标识、存储线程为当前线程、线程锁数量+1;
- 释放锁:判断当前线程是否等于存储线程,条件符合则线程锁数量-1,当线程锁数量=0时,修改占用标识,唤醒等待线程,将存储线程置为null;
// 可重入锁
class ReLock{
//是否占用
private boolean isLocked = false;
private Thread lockedBy = null; //存储线程
private int holdCount = 0;
//使用锁
public synchronized void lock() throws InterruptedException {
Thread t = Thread.currentThread();
while(isLocked && lockedBy != t) {
wait();
}
isLocked = true;
lockedBy = t;
holdCount ++;
}
//释放锁
public synchronized void unlock() {
if(Thread.currentThread() == lockedBy) {
holdCount --;
if(holdCount ==0) {
isLocked = false;
notify();
lockedBy = null;
}
}
}
public int getHoldCount() {
return holdCount;
}
}
==============使用===============
public class LockTest {
ReLock lock = new ReLock();
public void test1() throws InterruptedException {
lock.lock();
System.out.println(lock.getHoldCount());
test2();
lock.unlock();
System.out.println(lock.getHoldCount());
}
//可重入
public void test2() throws InterruptedException {
lock.lock();
System.out.println(lock.getHoldCount());
//...................
lock.unlock();
System.out.println(lock.getHoldCount());
}
public static void main(String[] args) throws InterruptedException {
LockTest lockTest= new LockTest();
lockTest.test1();
Thread.sleep(1000);
System.out.println(lockTest.lock.getHoldCount());
}
}