可重入锁

作者: 大海孤了岛 | 来源:发表于2017-05-08 22:05 被阅读129次

定义

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

作用

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

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

public class SpinLock {
    private AtomicReference<Thread> 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<Thread> 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

相关文章

  • ReentrantLock 源码分析

    锁的基本概念 可重入锁 Reentrant 就是可重入的意思,如果锁具备可重入性,则称作为可重入锁。像synchr...

  • java可重入锁

    可重入概念: java的可重入锁: 可重入锁的一种实现方式: 可重入锁的两种使用例子: 例子1: 例子2: 例子1...

  • Java 可重入锁 公平锁 读写锁

    1.可重入锁 如果锁具备可重入性,则称作为可重入锁。 像synchronized和ReentrantLock都是可...

  • Java中的各种锁

    一个线程中的多个流程能不能获取同一把锁:可重入锁和非可重入锁 可重入锁 可重入性:表明了锁的分配机制,是基于线程的...

  • 可重入锁和非可重入锁

    1 可重入锁 (ReentrantLock和synchronized)可重入锁指的是可重复可递归调用的锁,在外层使...

  • J.U.C-AQS-ReentrantLock

    ReentrantLock(可重入锁)和synchronized区别 可重入性 锁的实现(ReentrantLoc...

  • Java温故而知新之Lock与Synchronized

    两者比较: 相关概念解析 可重入锁如果锁具备可重入性,则称作为可重入锁。假设方法A需要获取锁,方法B也需要获取锁,...

  • 可重入锁-面试题:synchronized是可重入锁吗?

    前言 面试题:synchronized是可重入锁吗? 答案:synchronized是可重入锁。Reentrant...

  • 可重入锁-synchronized是可重入锁吗?Reentran

    前言 面试题:synchronized是可重入锁吗? 答案:synchronized是可重入锁。Reentrant...

  • 各种锁的概念

    锁的概念 可重入不可重入公平锁非公平锁锁中断通过一个故事理解可重入锁的机制 - 小勇DW3 - 博客园[https...

网友评论

本文标题:可重入锁

本文链接:https://www.haomeiwen.com/subject/rqpvtxtx.html