Java中的锁机制——可重入锁(递归锁)

目录

什么是可重入锁(递归锁)

sychronized版

lock版

为什么可重入锁能防止死锁 


 

什么是可重入锁(递归锁)

 

可重入锁也叫递归锁。它指的是同一个线程在外层获取锁之后,内层方法也会自动获取锁。lock和sychronized都是可重入锁

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

Java中的锁机制——可重入锁(递归锁)_第1张图片

看这个小例子,method01和02都是加锁的方法,根据可重入锁的原理,线程只要获得了01的锁就能访问02,即使02也是个带锁的方法。

Java中的锁机制——可重入锁(递归锁)_第2张图片

 

 

sychronized版

 

一个同步方法可进入另外一个同步方法

// Synchronized
public class Demo01 {
    public static void main(String[] args) {
        Phone phone = new Phone();
        new Thread(() -> {
            phone.sms();
        }, "A").start();
        new Thread(() -> {
            phone.sms();
        }, "B").start();
    }
}

class Phone {
    public synchronized void sms() {
        System.out.println(Thread.currentThread().getName() + "sms");
        call(); // 这里也有锁
    }

    public synchronized void call() {
        System.out.println(Thread.currentThread().getName() + "call");
    }
}

 

 

lock版

 

sms方法是一个lock锁方法,这个方法调用了call方法,而call方法也是个lock锁方法。可重入锁指一个线程只要能进入sms方法,就也能进入到call方法中


public class Demo02 {
    public static void main(String[] args) {
        Phone2 phone = new Phone2();
        new Thread(() -> {
            phone.sms();
        }, "A").start();
        new Thread(() -> {
            phone.sms();
        }, "B").start();
    }
}

class Phone2 {
    Lock lock = new ReentrantLock();

    public void sms() {
        lock.lock(); // 细节问题:lock.lock(); lock.unlock(); // lock 锁必须配对,否则就会死在里面
        try {
            System.out.println(Thread.currentThread().getName() + "sms");
            call(); // 这里也有锁
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void call() {
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + "call");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

 

 

为什么可重入锁能防止死锁 

 

这里举一个经典的例子。

public class Widget {
    public synchronized void doSomething(){
        // do something
    }
}
public class LoggingWidget extends Widget {
    public synchronized void doSomething() {
        super.doSomething();
    }
}

现在假设synchronized不是可重入锁。

Widget方法执行需要获得sync锁,默认情况下sync锁的是this,LoggingWidget是Widget的子类。当你执行LoggingWidget中的super.doSomething方法时它就会获得父类的this锁,但父类的this锁被子类抢走之后,没了锁就没法执行完毕。

这样就出现死锁的现象,子类让父类执行完释放锁,而父类的锁在子类这里没法执行完毕。

 

现在synchronized是可重入锁

当进入到LoggingWidget方法时,即使LoggingWidget方法里面有一另一个锁也没关系,它也能获得Widget类中的内容,由于Widget类执行完毕了,LoggingWidget便可以继续执行。

你可能感兴趣的:([线程与并发])