搜公众号
推荐 原创 视频 Java开发 开发工具 Python开发 Kotlin开发 Ruby开发 .NET开发 服务器运维 开放平台 架构师 大数据 云计算 人工智能 开发语言 其它开发 iOS开发 前端开发 JavaScript开发 Android开发 PHP开发 数据库
Lambda在线 > 小强的进阶之路 > 互联网JAVA面试常问问题(六)

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

小强的进阶之路 2019-01-22
举报



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

ReentrantLock

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

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代码中实现锁释放的过程。

Lock lock = new ReentrantLock();

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

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

}
finally
{  
 lock.unlock();
 }
非公平锁的实现
    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实现如下:

        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。其实也是一个多次尝试获取的过程。

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

公平锁的实现
  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方法首先尝试获取锁并且尝试将当前线程加入到一个队列中,所以公平锁其实是维护了一个队列,谁等待的时间最长,当线程空闲时候,就会最先获取资源:

    public final void acquire(int arg) {        
   if (!tryAcquire(arg) &&            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))            selfInterrupt();    }

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

   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中可以看到,线程可以重复获取已经持有的锁。

if (current == getExclusiveOwnerThread()) {                
   int
nextc = c + acquires;                
   if (nextc < 0) // overflow         throw new Error("Maximum lock count exceeded");    setState(nextc);                
   return true; }

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

                      





版权声明:本站内容全部来自于腾讯微信公众号,属第三方自助推荐收录。《互联网JAVA面试常问问题(六)》的版权归原作者「小强的进阶之路」所有,文章言论观点不代表Lambda在线的观点, Lambda在线不承担任何法律责任。如需删除可联系QQ:516101458

文章来源: 阅读原文

相关阅读

举报