可重入锁和不可重入锁

重入锁

如果某个线程试图获取一个已经由他自己持有的锁,这个请求可以成功,那么此时的锁就是可重入锁,重入锁的这种机制也说明了它是以”线程“为粒度获取锁,而不是以”调用“为粒度。重入常见的一个实现方法是,为每个锁关联一个持有者和持有计数值,当计数值为0时,这个锁会被认为没有被任何线程持有,当现场请求一个未被持有的锁时,jvm会把这个锁给这个线程,并几下这个锁的持有者,同时计数值置为1,如果同一个线程再次获取这个锁,计数值将递增,当线程退出同步代码块时,计数值将递减,当计数值为0时,锁将被释放。

常见的重入锁有synchronized和ReentrantLock(Reentrant本身就是重入的意思)

对于重入的理解见下面的例子(以synchronized为例):
首先定义一个父类SuperSyncObj,它的方法用synchronized修饰

public class SuperSyncObj {
    public synchronized void methodA(){
        System.out.println("super class running methodA");
    }
}

然后SyncObj继承这个父类,重写methodA,当然methodA也用synchronized修饰

public class SyncObj extends SuperSyncObj{
    @Override
    public  synchronized  void methodA() {
        System.out.println("methodA.....");
        try {
            super.methodA();
            methodB();
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

最后运行结果:

methodA.....
super class running methodA
methodB.....

可以看到,如果synchronized不具有重入性,执行super.methodA()的时候会出现死锁,但是synchronized具有重入性,执行到这一步的时候,锁会检测当前持有锁的线程,发现正是请求这个锁的线程本身,于是请求可以成功,同理methodA调用methodB, 因为也是同一个线程请求同一把锁,也可以成功

不可重入锁

不可重入锁和可重入锁相反,就是同一个线程多次请求同一把锁,会出现死锁

(1)现在模拟一个不可重入自旋锁。

public class UnreentrantLock {
    private AtomicReference<Thread> owner = new AtomicReference<Thread>();

    public void lock(){
        Thread current = Thread.currentThread();
        while(!owner.compareAndSet(null, current)){}
    }

    public void unlock(){
        Thread current = Thread.currentThread();
        owner.compareAndSet(current, null);
    }

}

1、若有同一线程两调用lock() ,会导致第二次调用lock位置进行自旋,产生了死锁
说明这个锁并不是可重入的。(在lock函数内,应验证线程是否为已经获得锁的线程)
2、若1问题已经解决,当unlock()第一次调用时,就已经将锁释放了。实际上不应释放锁。

我们使用UnreentrantLock:

public class UnreentrentLockObj {
    UnreentrantLock lock = new UnreentrantLock();

    public void lockFunc(){
        lock.lock();
        System.out.println("lockFunc...");
        lockFunc2();
        lock.unlock();
    }

    public void lockFunc2(){
        lock.lock();
        System.out.println("lockFunc2...");
        lock.unlock();
    }

    public static void main(String[] args) {
        final UnreentrentLockObj obj = new UnreentrentLockObj();
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                obj.lockFunc();
            }
        });
        thread.start();
    }
}

这时候会发现,在调用lockFunc2()的时候出现了死锁

(2)改进一下,改为可重入锁

public class UnreentrantLock {
    private AtomicReference<Thread> owner = new AtomicReference<Thread>();
    private int count = 0;

    public void lock(){
        Thread current = Thread.currentThread();
        if(current == owner.get()){
            count++;
            return;
        }
        while(!owner.compareAndSet(null, current)){}
    }

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

这样上面的调用就不会发生死锁

  • 4
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值