可重入锁是什么?

本文内容如有错误、不足之处,欢迎技术爱好者们一同探讨,在本文下面讨论区留言,感谢。

简介

可重入锁在Java中有synchronizeReentrantLock,其中synchronize是在JVM中进行可重入控制,ReentrantLock是在代码中实现可重入控制。

在Java 5.0中,增加了一个称为ReentrantLock的新功能,以增强内部锁定功能。在此之前,“synchronized" 和 "volatile”是实现并发的手段。

public synchronized void doAtomicTransfer(){
     //进入同步代码块 获取对此对象的锁定。
    operation1()
    operation2();    
} // 退出同步代码块, 释放(release)对此对象的锁定。

同步使用内部锁或监视器。Java中的每个对象都有一个与之关联的固有锁。每当线程尝试访问同步的块或方法时,它都会获取该对象的固有锁定或监视器。在使用静态方法的情况下,线程获取对类对象的锁定。就编写代码而言,内在锁定机制是一种干净的方法,对于大多数用例而言,它是相当不错的。

那么,为什么我们需要显式锁的附加功能?

内部锁定机制可能具有一些功能限制,例如:

  1. 无法中断等待获取锁的线程(lock Interruptibly)。
  2. 如果不愿意永远等待,就无法尝试获取锁(try lock)。
  3. 无法实现非块结构的锁定规则,因为必须在获取它们的同一块中释放固有锁。
ReentrantLock
void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
.....

ReentrantLock

中断锁获取

可中断的锁获取允许在可取消的活动中使用锁。

该的lockInterruptibly方法可以让我们尝试,同时可中断获取锁。因此,基本上,它允许线程立即响应从另一个线程发送给它的中断信号。

例子:假设有一条共享线路来发送消息。希望以这样一种方式设计它:如果另一个线程来了并中断当前线程,则锁应释放并执行退出或关闭操作以取消当前任务。

public boolean sendOnSharedLine(String message) throws InterruptedException{
	lock.lockInterruptibly();
	try{
		return cancellableSendOnSharedLine(message);
	} finally {
		lock.unlock();
	}
}

private boolean cancellableSendOnSharedLine(String message){
.......
可重入设计的意义

广义上的可重入锁指的是可重复可递归调用的锁,在外层使用锁之后,在内层仍然可以使用,并且不发生死锁(前提得是同一个对象或者class),这样的锁就叫做可重入锁。

这样可以避免每次获取锁之后,在操作资源后,释放锁再获取锁,每次释放锁再获取锁的调度切换是很耗资源。

避免子类改写了父类的 synchronized 方法,调用子类时发生死锁。

不可重入锁
class UnReentantLock{
    private AtomicReference<Thread> owner = new AtomicReference<>();

    /**
     * 获取锁
     */
    public void lock(){
        Thread current = Thread.currentThread();
        // 自旋锁实现,同样可以使用do while
        //for (; ; ) {
        //    if (!owner.compareAndSet(null, current)) {
        //        return ;
        //    }
        //}
        do {

        } while (!owner.compareAndSet(null, current));
        return;
    }

    /**
     * 释放锁
     */
    public void unLock(){
        Thread current = Thread.currentThread();
        owner.compareAndSet(current, null);
    }
}

这里使用了原子引用和CAS

CAS的原理是Unsafe类和自旋锁,上面已经展示自旋锁如何使用,下面图片是Unsafe类中的一个native方法的c++实现,可以查看 JVM 本地方法栈(native method stack)解释
Unsafe方法内部实现

不可重入改为可重入

可重入锁实现是通过计数器来实现可重入多次。


ReentrantLock lock = new ReentrantLock();
lock.lock();
lock.lock();

try{
...
}finally{
	lock.unlock();
	lock.unlock();
}

上面的代码可以说明,加了几次lock()就要执行几次unlock();

下面是改造代码:
借鉴:https://blog.csdn.net/wuseyukui/article/details/72312574

class NumberReentrantLock{
    private AtomicReference<Thread> owner = new AtomicReference<>();
    private int lockNumber = 0;

    public void lock(){
        Thread current = Thread.currentThread();
        // 使用== 表示是同一个引用
        if (current == owner.get()) {
            lockNumber++;
            return;
        }
        do {

        } while (!owner.compareAndSet(null, current));
    }

    public void unlock(){
        Thread current = Thread.currentThread();
        if (current == owner.get()) {
            if (lockNumber == 0) {
                owner.compareAndSet(current, null);
            }else{
                lockNumber--;
            }
        }
    }
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值