可重入锁&不可重入锁

1,298 阅读2分钟

这是我参与8月更文挑战的第11天,活动详情查看:8月更文挑战

什么是可重入锁?

  • 同一个线程可以重入上锁的代码段,不同的线程则需要进行阻塞

  • Java的可重入锁有:ReentrantLock(显式的可重入锁)、synchronized(隐式的可重入锁)

  • 可重入锁的最大作用是避免死锁

  • 同步锁可以再次进入(同一个线程)

就比如锁方法A上锁lock而方法B也上锁lock,并且在方法A中调用方法B,不会出现死锁的情况,因为它们处于同一个线程用的是同一把锁,所以可重入锁运行再次进入。

不可重入锁

这段代码模拟了不可重入锁的构造,运行后发现出现死锁的情况这是因为在method1方法中上锁之后没有释放,在调用method2的时候内部再一次调用了lock方法,上次method1调用lock方法的时候已经将isLock = true,所以一直处于while循环当中

public class NotReentrantLockDemo {
    static MyLock lock = new MyLock();
    public static void main(String[] args) throws InterruptedException {
        new NotReentrantLockDemo().method1();
    }
    public void method1() throws InterruptedException {
        lock.lock();
        System.out.println("this is method1");
        method2();
        lock.unlock();
    }
    public void method2() throws InterruptedException {
        lock.lock();
        System.out.println("this is method2");
        lock.unlock();
    }
}

class MyLock{
     private   boolean isLock = false;
    public synchronized void lock() throws InterruptedException {
        while(isLock){
            wait();
        }
        isLock = true;
    }
    public synchronized  void unlock(){
        isLock = false;
        notify();
    }
}

测试结果: 仅打印this is method1后出现阻塞

可重入锁

public class ReentrantLockDemo {
    static MyReentrantLock lock = new MyReentrantLock();
    public static void main(String[] args) throws InterruptedException {
        new ReentrantLockDemo().method1();
    }
    public void method1() throws InterruptedException {
        lock.lock();
        System.out.println("this is method1");
        method2();
        lock.unlock();
    }
    public void method2() throws InterruptedException {
        lock.lock();
        System.out.println("this is method2");
        lock.unlock();
    }
}
class MyReentrantLock{
    private  boolean isLock = false;
    Thread lockThread = null;
    int lockCount = 0;
    public synchronized void  lock() throws InterruptedException {
        while(isLock && Thread.currentThread()!=lockThread){
            wait();
        }
        isLock = true;
        lockCount++;
        lockThread = Thread.currentThread();
    }
    public synchronized void unlock(){
        if(lockThread == Thread.currentThread()){
            lockCount --;
            if(lockCount == 0){
                isLock = false;
                notify();
            }
        }
    }
}

测试结果:

this is method1

this is method2

解析

可以看到与不可重入锁相比,可重入锁新增两个属性上锁的线程lockThread和上锁的次数lockCount,通过这两个变量的控制实现防止死锁的目的。

synchronized是可重入锁

public class ReentrantLockDemo {
    public static void main(String[] args) {
        Resource resource = new Resource();
        new Thread(()->{resource.method1();}).start();
    }

}
class Resource{
    public  synchronized  void method1(){
        System.out.println("this is method1");
        System.out.println(Thread.currentThread().getId());
        method2();
    }
    public synchronized  void method2(){
        System.out.println("this is  method2");
        System.out.println(Thread.currentThread().getId());
    }
}

运行结果:

this is method1

12

this is method2

12

ReentrantLock是可重入锁

public class ReentrantLockDemo {


    public static void main(String[] args) throws InterruptedException {

        new Thread(new Task()).start();
    }

}
class Task implements Runnable {
    static Lock lock = new ReentrantLock();

    @Override
    public void run() {
        lock.lock();
        process1();
        lock.unlock();
    }

    public void process1() {

        System.out.println("this is process1" + "/t id=" + Thread.currentThread().getId());
        process2();

    }

    public void process2() {
        lock.lock();
        System.out.println("this is process2" + "/t id=" + Thread.currentThread().getId());
        lock.unlock();
    }
}

this is process1/t id=13

this is process2/t id=13