前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >互联网JAVA面试常问问题(六)

互联网JAVA面试常问问题(六)

作者头像
程序员小强
发布2019-09-20 16:54:14
3020
发布2019-09-20 16:54:14
举报

前几篇文章,都介绍了JAVA面试中锁相关的知识。其实关于JAVA锁/多线程,你还需要知道了解关于ReentrantLock的知识,本文将从源码入手,介绍可重入锁的相关知识。

ReentrantLock

先来看看ReentrantLock的源码,部分代码块用省略号代替,后面会详细展开介绍:

代码语言:javascript
复制
public class ReentrantLock implements Lock, java.io.Serializable {    

    private static final long serialVersionUID = 7373984872572414699L;    
    
    /** Synchronizer providing all implementation mechanics */
   private final Sync sync;    
   
    abstract static class Sync extends AbstractQueuedSynchronizer {        
        
        private static final long serialVersionUID = -5179523762034025860L;        /**
        * Performs {@link Lock#lock}. The main reason for subclassing
        * is to allow fast path for nonfair version.
        */
       abstract void lock();        /**
        * Performs non-fair tryLock.  tryAcquire is implemented in
        * subclasses, but both need nonfair try for trylock method.
        */
       final boolean nonfairTryAcquire(int acquires) {
           .......
       }        
        protected final boolean tryRelease(int releases) {
           int c = getState() - releases;            
            if (Thread.currentThread() != getExclusiveOwnerThread())                
                throw new IllegalMonitorStateException();
           boolean free = false;            
            if (c == 0) {
             free = true;
             setExclusiveOwnerThread(null);
           }
            
           setState(c);            
            return free;
       }        
                
        protected final boolean isHeldExclusively() {            
            // While we must in general read state before owner,
           // we don't need to do so to check if current thread is owner
           return getExclusiveOwnerThread() == Thread.currentThread();
       }        
        ........
   }    /**
    * Sync object for non-fair locks
    */
   static final class NonfairSync extends Sync {
      .......
   }    /**
    * Sync object for fair locks
    */
   static final class FairSync extends Sync {
      ...........
   }  ..........
}

可以看出可重入锁的源码中,其实实现了公平锁和非公平锁。

ReentrantLock中有一个静态内部抽象类Sync,然后有NonfairSync和FairSync两个静态类继承了Sync。

其中Sync继承了AQS(AbstractQueuedSynchronizer),接下来的文章中会介绍详细AQS。

我们在使用可重入锁的时候,需要明显的加锁和释放锁的过程。一般在finally代码中实现锁释放的过程。

代码语言:javascript
复制
Lock lock = new ReentrantLock();

Condition condition = lock.newCondition();
lock.lock();

try {  while(条件判断表达式) {
     condition.wait();
 } 
// 处理逻辑

} 
finally 
{   
 lock.unlock();
 }
非公平锁的实现
代码语言:javascript
复制
    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() {            
            if (compareAndSetState(0, 1))
               setExclusiveOwnerThread(Thread.currentThread());            
            else
               acquire(1);
       }        
        protected final boolean tryAcquire(int acquires) {            
            return nonfairTryAcquire(acquires);
       }
   }

可以看出,非公平锁在执行lock的时候,会用CAS来尝试将锁状态改成1,如果修改成功,则直接获取锁,用setExclusiveOwnerThread方法讲当前线程设置为自己。如果没有修改成功,则会执行acquire方法来尝试获取锁。其中,nonfairTryAcquire实现如下:

代码语言:javascript
复制
        final boolean nonfairTryAcquire(int acquires) {            
        final Thread current = Thread.currentThread();           
        int c = getState();            
        if (c == 0) {                
        if (compareAndSetState(0, acquires)) {
                   setExclusiveOwnerThread(current);                    
                    return true;
               }
           }            
         else if (current == getExclusiveOwnerThread()) {                
            int nextc = c + acquires;                
            if (nextc < 0) // overflow
                   throw new Error("Maximum lock count exceeded");
              setState(nextc);                
             return true;
           }            
             return false;
       }

可以看出这个方法,其实也是在用CAS尝试将线程状态置为1。其实也是一个多次尝试获取的过程。

所以,对于非公平锁,当一线程空闲时候,其他所有等待线程拥有相同的优先级,谁先争抢到资源即可以获取到锁。

公平锁的实现
代码语言:javascript
复制
  static final class FairSync extends Sync {        
        private static final long serialVersionUID = -3000897897090466540L;        
    
    final void lock() {
           acquire(1);
       }        /**
        * Fair version of tryAcquire.  Don't grant access unless
        * recursive call or no waiters or is first.
        */
       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;
               }
           }            
            else if (current == getExclusiveOwnerThread()) {                
               int nextc = c + acquires;                
               if (nextc < 0)                    
                   throw new Error("Maximum lock count exceeded");
              setState(nextc);                
               return true;
           }            
               return false;
       }
   }

主要看当公平锁执行lock方法的时候,会调用 acquire方法, acquire方法首先尝试获取锁并且尝试将当前线程加入到一个队列中,所以公平锁其实是维护了一个队列,谁等待的时间最长,当线程空闲时候,就会最先获取资源:

代码语言:javascript
复制
    public final void acquire(int arg) {        
    if (!tryAcquire(arg) &&
           acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
           selfInterrupt();
   }

如果想了解acquireQueued的话,可以参照一下代码:

代码语言:javascript
复制
   final boolean acquireQueued(final Node node, int arg) {
       boolean failed = true;
       try {
           boolean interrupted = false;
           for (;;) {
               final Node p = node.predecessor();
               if (p == head && tryAcquire(arg)) {
                   setHead(node);
                   p.next = null; // help GC
                   failed = false;
                   return interrupted;
               }
               if (shouldParkAfterFailedAcquire(p, node) &&
                   parkAndCheckInterrupt())
                   interrupted = true;
           }
       } finally {
           if (failed)
               cancelAcquire(node);
       }
   }

综上,可重入锁利用CAS原理实现了公平锁和非公平锁,为什么叫做可重入锁呢?其实在代码方法tryAcquire中可以看到,线程可以重复获取已经持有的锁。

代码语言:javascript
复制
if (current == getExclusiveOwnerThread()) {                
    int nextc = c + acquires;                
    if (nextc < 0) // overflow
        throw new Error("Maximum lock count exceeded");
   setState(nextc);                
    return true;
}

今天小强从源码的角度分析了ReentrantLock,希望对正在学习JAVA的你有所帮助。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-01-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 MoziInnovations 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • ReentrantLock
  • 非公平锁的实现
  • 公平锁的实现
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档