面试题:你知道ReentrantLock吗?回答思路

答:第一步:平常没用过,synchronized无法满足所有的场合。ReentrantLock是java类实现的,而synchronized是jvm层实现的。ReentrantLock则更灵活但是也很危险。它可以中断正在请求锁的线程,也可以让线程无限请求等待。

第二步:然后我简单说下它的原理。
(1)它的原理是基于AQS(AbstractQueuedSynchronizer ),多个线程抢锁时,如果争抢失败则会进入阻塞队列。等待唤醒,重新尝试加锁。
(2) AQS的子类有公平锁FairSync和非公平锁NofairSync,ReentrantLock的无参构造默认是非公平锁,有参构造参数是true可以设置成公平锁。
(3)公平锁:ReentrantLock调用lock方法,最终会调用Sync类的tryAcquire函数,获取资源。当前线程只有在队列为空或者时队首节点的时候,才能获取资源,否则会被加入到阻塞队列中。
(4)非公平锁:也是调用sync的tryAcquire,不同的是tryAcquire还会调用nofairTryAcquire。
(5)公平锁和非公平锁的区别:非公平锁在调用NonfairSync的lock的时候就会马上进行CAS抢锁,抢不到就和公平锁一样进入tryAcquire方法尝试抢锁,如果发现锁被释放了(state==0),非公平锁马上CAS抢锁,而不会管阻塞队列里是否有线程等待。而公平锁会排队等待。

ReentrantLock:

public ReentrantLock() {
	sync = new NonfairSync();
}

public ReentrantLock(boolean fair) {
	sync = fair ? new FairSync() : new NonfairSync();
}

公平锁:FairSync  

 static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;
 
        final void lock() {
            acquire(1);
        }
       //尝试直接获取锁,返回值是boolean,代表是否获取到锁
       //返回true:
        //1.没有线程在等待锁;
        //2.重入锁,线程本来就持有锁,也就可以理所当然可以直接获取
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                //关键点
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            //判断是有锁的线程是否为当前线程。
            //可重入就在这里体现的,同一个线程多次调用lock方法
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
    }

 非公平锁:NonfairSync 

static final class NonfairSync extends Sync {
     private static final long serialVersionUID = 7316153563782823691L;
 
     /**
      * Performs lock.  Try immediate barge, backing up to normal
      * acquire on failure.
      */
     final void lock() {
         //不管AQS队列有没有等待的线程,直接开始抢
         if (compareAndSetState(0, 1))
             setExclusiveOwnerThread(Thread.currentThread());
         else
            acquire(1);
     }
 
     protected final boolean tryAcquire(int acquires) {
         return nonfairTryAcquire(acquires);
     }
 }

记忆力障碍患者的回答(我就是):

第一步:平常没用过,synchronized用的比较多,但是不能满足所有要求。ReentrantLock更加灵活,它可以中断正在请求锁的线程等操作。

第二步:简单说下原理,内部有两种实现,一种是公平锁,一种是非公平锁,默认是非公平锁,它有个tryAcquire方法来尝试获取锁。

      然后呢,非公平锁会尝试两次抢锁,第一次抢不到会等待锁释放,一释放马上再CAS抢锁,再抢失败就进入阻塞队列等待唤醒。

回答思路:1.说明ReentrantLock是Java类,它与synchronized相比能多做什么事情; 2.说下内部的公平锁和非公平锁,重点说下非公平锁有两次“霸道”抢锁。

 

欢迎一起交流学习!

 

 

参考资料:https://blog.csdn.net/zycxnanwang/article/details/105321401

https://blog.csdn.net/o9109003234/article/details/106066537

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值