可重入&不可重入锁的原理

2023-09-10 八股文

前置知识

  • wait:让线程进入等待状态,直到notifynotifyAll通知才会唤醒,某线程调用此方法会释放锁;
  • notify:唤醒在对象监视器上等待的单个线程,选择是任意的
  • notifyAll:唤醒全部线程
  • 可重入锁:也叫递归锁,在外层使用锁之后,在内层仍然可以使用,并且不发生死锁(注意一个锁只能被一个线程获取到)
  • 不可重入锁:当前线程执行某个方法已经获取了该锁,那么在方法中尝试再次获取锁时,就会获取不到被阻塞

# 不可重入锁简单实现

public class UnreentrantLock {

    // false代表解锁状态,true代表加锁状态
    private boolean isLocked = false;

    /** 加锁 */
    public synchronized void lock() throws InterruptedException {
        while (isLocked) {
            wait(); // 获取不到锁进入等待状态
        }
        isLocked = true; // 加锁
    }

    /** 解锁 */
    public synchronized void unlock() {
        isLocked = false;
        notify(); //唤醒对象锁池里面的一个线程
    }
}

# 可重入式锁简单实现







 


 




 


 



 
 




 
 
 
 
 
 
 
 






public class ReentrantLock {

    /** false代表解锁状态,true代表加锁状态 */
    private boolean isLocked = false;

    /** 用于记录是不是重入的线程 */
    private Thread lockedOwner = null;

    /** 累计加锁次数,加锁一次累加1,解锁一次减少1 */
    private int lockedCount = 0;

    /** 加锁 */
    public synchronized void lock() throws InterruptedException {
        // 1.获取当前正在执行的线程
        Thread thread = Thread.currentThread();

        // 2.如果不是同个线程获取锁,则进入等待
        while (isLocked && lockedOwner != thread) {
            wait(); // 获取不到锁进入等待状态
        }
        isLocked = true;
        lockedOwner = thread;
        lockedCount++;
    }

    /** 解锁 */
    public synchronized void unlock() {
        Thread thread = Thread.currentThread();
        //线程A加的锁,只能由线程A解锁,其他线程B不能解锁
        if (thread == this.lockedOwner) {
            lockedCount--;
            // 当所有锁都解除锁定才释放锁
            if (lockedCount == 0) {
                isLocked = false;
                lockedOwner = null;
                notify(); //唤醒对象锁池里面的一个线程
            }
        }
    }
}

# 测试

可重入式锁测试方法

如图所示调用methodA方法获取到锁后,调用methodB方法,methodB方法会尝试获取锁,如果是可重入式锁就能获取到,如果是不可重入锁就无法获取到

public class Main {
//    private final UnreentrantLock myLock = new UnreentrantLock(); // 使用不可重式入锁会发生死锁
    private final ReentrantLock myLock = new ReentrantLock(); // 使用可重入式锁就不会发送此问题

    //加锁建议在try里面,解锁建议在finally
    public void methodA() {
        try {
            myLock.lock();
            System.out.println("methodA方法被调用");
            methodB();
        } catch (InterruptedException e) {
            e.fillInStackTrace();
        } finally {
            myLock.unlock();
        }
    }

    public void methodB() {
        try {
            myLock.lock();
            System.out.println("methodB方法被调用");
        } catch (InterruptedException e) {
            e.fillInStackTrace();
        } finally {
            myLock.unlock();
        }
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Main().methodA(); //演示的是同个线程
        }
    }
}
上次更新: 5 个月前