可重入锁

定义

可重入锁,也叫做递归锁,指的是在同一线程内,外层函数获得锁之后,内层递归函数仍然可以获取到该锁。换一种说法:同一个线程再次进入同步代码时,可以使用自己已获取到的锁。

作用

防止在同一线程中多次获取锁而导致死锁发生。

如下,我们通过自旋锁来判断是否会发生死锁:

public class SpinLock {
    private AtomicReference sign = new AtomicReference<>();
    public void lock(){
        Thread current = Thread.currentThread();
        while (!sign.compareAndSet(null,current)){
        }
    }
    public void unlock(){
        Thread cur = Thread.currentThread();
        sign.compareAndSet(cur,null);
    }
}

public class SpinLockDemo {
    private SpinLock lock =  new SpinLock();
    class Widget{
        public void doSomething(){
            lock.lock();
            System.out.println("Widget calling doSomething");
            lock.unlock();
        }
    }

    class LoggingWidget extends Widget {
        @Override
        public void doSomething() {
            lock.lock();
            System.out.println("LoggingWidget calling doSomething");
            super.doSomething();
            lock.unlock();
        }
    }

    public static void main(String[] args){
        SpinLockDemo spinLockDemo = new SpinLockDemo();
        SpinLockDemo.Widget widget = spinLockDemo.new LoggingWidget();
        widget.doSomething();
    }
}

输出结果:
LoggingWidget calling doSomething

我们可以看到在LoggingWidget类中doSomething方法时,通过锁进入临界区,并在临界区中调用了父类的该方法,而父类的方法要获取到同一个锁,被阻塞,导致死锁发生。

常见的可重入锁

  • Synchronized关键字:
public class SynchronizedDemo {
     class Widget{
        public synchronized void doSomething(){
            System.out.println("Widget calling doSomething...");
        }
    }

     class LoggingWidget extends Widget{
        @Override
        public synchronized void doSomething() {
            System.out.println("LoggingWidget calling doSomething");
            super.doSomething();
        }
    }

    public static void main(String[] args){
         SynchronizedDemo synchronizedDemo = new SynchronizedDemo();
        SynchronizedDemo.Widget widget = synchronizedDemo.new LoggingWidget();
        widget.doSomething();
    }
}

输出结果:
LoggingWidget calling doSomething
Widget calling doSomething...

根据结果,我们可以看到Synchronized关键字是可重入锁。

  • ReetrantLock:
public class ReentrantLockDemo {
    private Lock lock =  new ReentrantLock();
    class Widget{
        public void doSomething(){
            lock.lock();
            System.out.println("Widget calling doSomething");
            lock.unlock();
        }
    }

    class LoggingWidget extends Widget {
        @Override
        public void doSomething() {
            lock.lock();
            System.out.println("LoggingWidget calling doSomething");
            super.doSomething();
            lock.unlock();
        }
    }

    public static void main(String[] args){
        ReentrantLockDemo reentrantLockDemo = new ReentrantLockDemo();
        Widget widget = reentrantLockDemo.new LoggingWidget();
        widget.doSomething();
    }

}

输出结果:
LoggingWidget calling doSomething
Widget calling doSomething

根据结果,我们可以看出ReetrantLock锁时可重入的。

实现可重入锁

为每个锁关联一个获取计数器和一个所有者线程,当计数值为0时,这个锁就被认为是没有被任何线程所占有的。当线程请求一个未被持有的锁时,计数值将会递增。而当线程退出同步代码时,计数器会相应地递减。当计数值为0时,则释放该锁。

如下:我们通过修改自旋锁来实现一个可重入的自旋锁

public class SpinLockDemo {
    private MySpinLock lock =  new MySpinLock();
    class Widget{
        public void doSomething(){
            lock.lock();
            System.out.println("Widget calling doSomething");
            lock.unlock();
        }
    }

    class MySpinLock{
        private AtomicReference owner = new AtomicReference<>();
        private int count = 0;
        public void lock(){
            Thread cur = Thread.currentThread();
            if (cur == owner.get()){
                count ++;
                return;
            }
            while (! owner.compareAndSet(null,cur)){

            }
        }

        public void unlock(){
            Thread cur = Thread.currentThread();
            if (cur == owner.get()){
                if (count != 0){
                    count --;
                } else {
                    owner.compareAndSet(cur,null);
                }
            }
        }
    }

    class LoggingWidget extends Widget {
        @Override
        public void doSomething() {
            lock.lock();
            System.out.println("LoggingWidget calling doSomething");
            super.doSomething();
            lock.unlock();
        }
    }

    public static void main(String[] args){
        SpinLockDemo spinLockDemo = new SpinLockDemo();
        SpinLockDemo.Widget widget = spinLockDemo.new LoggingWidget();
        widget.doSomething();
    }
}

输出结果:
LoggingWidget calling doSomething
Widget calling doSomething

你可能感兴趣的:(可重入锁)