`
zhangwei_david
  • 浏览: 469589 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

JDK8 ReentrantReadWriteLock源码分析

阅读更多
ReentrantReadWriteLock中的state代表了读锁的数量和写锁的持有与否,整个结构如下: 
 
 
在本文中对AQS部分源码不在讲解,可以参考  AbstractQueuedSynchronizer源码分析    
首先从读锁开始看起
 readLock.lock()
   

 

/**
  * 获取读锁
  * 如果写锁没有被其他线程占有,获取读锁后立即返回
  * 如果写锁被其他线程占有,则当前线程挂起直到获取到读锁
  **/
 public void lock() {
            sync.acquireShared(1);
        }

  public final void acquireShared(int arg) {
        if (tryAcquireShared(arg) < 0)
            doAcquireShared(arg);
    }

  
  /**
    * 如果写锁被占用则失败
    * 否则该线程具有获取锁的资格,首先根据不同的队列策略判断是否需要被挂起,如果不需要
    * 则通过CAS操作尝试获取锁,如果可以获取锁则需要更新计数;注意在这步操作中不进行重入处理
    * 如果线程需要挂起,或达到上限或CAS操作失败都会进入完整获取读锁的循环中
    **/
  protected final int tryAcquireShared(int unused) {
            //当前线程
            Thread current = Thread.currentThread();
            //获取同步状态
            int c = getState();
            //写锁被占用,且不是当前线程;则获取读锁失败
            if (exclusiveCount(c) != 0 &&
                getExclusiveOwnerThread() != current)
                return -1;
             //获取读锁目前被占用数量   
            int r = sharedCount(c);
            //如果读取器不需要排队且读锁占用数量没有达到上限则通过CAS尝试获取读锁
            if (!readerShouldBlock() &&
                r < MAX_COUNT &&
                compareAndSetState(c, c + SHARED_UNIT)) {
                //如果读区锁为0,表示还没有任何读取器占用锁,则将当前线程设置为第一个读取器,其持有锁的数量为1个
                if (r == 0) {
                    firstReader = current;
                    firstReaderHoldCount = 1;
                    //如果已经有线程占用读锁,且当前线程和第一个占用读锁线程相同则其持有锁的数量自增
                } else if (firstReader == current) {
                    firstReaderHoldCount++;
                } else {
                   //如果已经有线程占用了读锁,但是不是当前线程
                   //最后一个成功获取读锁的线程占用读锁的数量计数器
                    HoldCounter rh = cachedHoldCounter;
                    //如果不为null且不是当前线程,则将其更新为当前线程的读锁计数器
                    if (rh == null || rh.tid != getThreadId(current))
                        cachedHoldCounter = rh = readHolds.get();
                     //如果rh获取锁的数量为0则表示rh是新建对象,将其加入到readHolds中   
                    else if (rh.count == 0)
                        readHolds.set(rh);
                        //持有读锁数量自增
                    rh.count++;
                }
                //返回1
                return 1;
            }
            //如果读锁需要排队,或者达到读锁上限或者CAS失败都会进行充分获取锁重试循环
            return fullTryAcquireShared(current);
        }


        /**
          * 获取读锁的完整版本,处理CAS遗漏以及在上一步操作中没有处理的重入问题
          *
          **/
        final int fullTryAcquireShared(Thread current) {
           
            HoldCounter rh = null;
           
            for (;;) {
               //获取同步状态
                int c = getState();
                //如果写锁被占用且不是当前线程则返回-1
                if (exclusiveCount(c) != 0) {
                    if (getExclusiveOwnerThread() != current)
                        return -1;
                 //写锁没有被占用判断当前线程是否需要挂起,如果需要挂起   
                } else if (readerShouldBlock()) {
                    // Make sure we're not acquiring read lock reentrantly
                    if (firstReader == current) {
                        // assert firstReaderHoldCount > 0;  
                    } else {
                       //当前第一个读取器不是当前线程,即别的线程占有了读锁
                        if (rh == null) {
                        //将最后一个成功获取读锁的线程计数器赋值给rh
                            rh = cachedHoldCounter;
                            //如果还没有线程获取读锁,或者最后一个获取读锁的不是当前线程则获取当前线程的计数器
                            if (rh == null || rh.tid != getThreadId(current)) {
                                rh = readHolds.get();
                                //如果当前线程计数器,中获取读锁的数量为0则将其删除
                                if (rh.count == 0)
                                    readHolds.remove();
                            }
                        }
                        //当前线程没有获取到读锁
                        if (rh.count == 0)
                            return -1;
                    }
                }
                //如果读锁达到上限抛出异常
                if (sharedCount(c) == MAX_COUNT)
                    throw new Error("Maximum lock count exceeded");
                  //CAS操作,将写锁清0,如果成功则表示写锁没有被占用
                if (compareAndSetState(c, c + SHARED_UNIT)) {
                    if (sharedCount(c) == 0) {
                        firstReader = current;
                        firstReaderHoldCount = 1;
                    } else if (firstReader == current) {
                        firstReaderHoldCount++;
                    } else {
                        if (rh == null)
                            rh = cachedHoldCounter;
                        if (rh == null || rh.tid != getThreadId(current))
                            rh = readHolds.get();
                        else if (rh.count == 0)
                            readHolds.set(rh);
                        rh.count++;
                        cachedHoldCounter = rh; // cache for release
                    }
                    return 1;
                }
            }
        }

         
readLock.unlock()

 public void unlock() {
            sync.releaseShared(1);
        }

 public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }


 protected final boolean tryReleaseShared(int unused) {
          //当前线程
            Thread current = Thread.currentThread();
            //第一个读取器是当前线程
            if (firstReader == current) {
                // assert firstReaderHoldCount > 0;
                //如果第一个读取器持有的读锁数量为1,则将第一个读取器设置为null,否则将其持有锁的数量自减
                if (firstReaderHoldCount == 1)
                    firstReader = null;
                else
                    firstReaderHoldCount--;
             //如果第一读取器不是当前线程       
            } else {
               //最后一个成功获取读锁的计数器
                HoldCounter rh = cachedHoldCounter;
                //如果计数器为null或最后一个成功获取读锁的不是当前线程
                if (rh == null || rh.tid != getThreadId(current))
                    //将当前线程的计数器赋值给rh
                    rh = readHolds.get();
                 //当前线程持有读锁的数量   
                int count = rh.count;
                //如果持有锁的数量小于或等于1则将该线程从readHolds中删除
                if (count <= 1) {
                    readHolds.remove();
                    //如果小于或等于0抛出异常,因为至少持有一把读锁
                    if (count <= 0)
                        throw unmatchedUnlockException();
                }
                //如果持有多把读锁则,持有锁的数量自减
                --rh.count;
            }

            for (;;) {
               //获取同步状态
                int c = getState();
                //计算释放一个读锁后读锁的数量
                int nextc = c - SHARED_UNIT;
                //CAS更新
                if (compareAndSetState(c, nextc))
                    //如果本次释放后,读锁没有被占用则返回成功,否则返回失败
                    return nextc == 0;
            }
        }          
  writeLock.lock()
public void lock() {
            sync.acquire(1);
        }

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

 /*
  *尝试获取写锁,
  * 如果读锁占用数量不为0或写锁占用数量不为0且不是当前线程拥有写锁,则获取写锁失败
  * 如果写锁饱和同样失败
  * 否则当前线程具有获取锁的资格
  */
protected final boolean tryAcquire(int acquires) {
             //当前线程
            Thread current = Thread.currentThread();
            //获取同步状态
            int c = getState();
            //计算写锁数量
            int w = exclusiveCount(c);
            //如果有锁被占用(读锁或写锁)
            if (c != 0) {
                 //如果写锁没有被占用,则表示当前有读锁被占用,获取写锁失败;如果写锁被占用且不是当前线程占用则 当前线程获取写锁失败
                if (w == 0 || current != getExclusiveOwnerThread())
                    return false;
                 //如果已经占有的写锁数量加上本次占用的和超过上限则抛出异常   
                if (w + exclusiveCount(acquires) > MAX_COUNT)
                    throw new Error("Maximum lock count exceeded");
                //写锁是当前线程占有,重入则将占用写锁的数量加上本次占用数量
                setState(c + acquires);
                //返回成功
                return true;
            }
            //如果c==0表示读锁,写锁都没有被占用,判断写锁是否需要挂起,如果需要则返回获取锁失败,如果不需要则CAS竞争锁,失败返回false
            if (writerShouldBlock() ||
                !compareAndSetState(c, c + acquires))
                return false;
             //不需要挂起且竞争到了写锁则将独占锁的拥有者设置为当前线程   
            setExclusiveOwnerThread(current);
            
            return true;
        }    
writeLock.unlock()
  public void unlock() {
            sync.release(1);
        }

    public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    } 
    
    protected final boolean tryRelease(int releases) {
          //如果当前线程没有持有写锁则抛出异常
            if (!isHeldExclusively())
                throw new IllegalMonitorStateException();
             //计算释放写锁后的状态   
            int nextc = getState() - releases;
            //如果释放当前写锁后再无写锁占用,则free=true表示写锁完全释放,如果还有占用则free=false
            boolean free = exclusiveCount(nextc) == 0;
            //如果是完全释放则将当前线程设置为null
            if (free)
                setExclusiveOwnerThread(null);
             //更新同步状态   
            setState(nextc);
            return free;
        }  
    //判断当前占有写锁的线程是否是当前线程,如果是返回true,否则返回false    
    protected final boolean isHeldExclusively() {
            return getExclusiveOwnerThread() == Thread.currentThread();
        }       
 
 
 
 
 
1
1
分享到:
评论

相关推荐

    jdk-8u60源码

    jdk 8u60 源码下载: 导入请阅读IMPORT_README Main: sun.misc.Launcher

    jdk源码 jdk源码

    jdk源码, jdk源码 jdk源码, jdk源码, jdk源码, jdk源码 jdk源码 jdk源码 jdk源码

    JDK8HashMap源码

    精确的版本号是jdk-8u181。想不通,竟然很多人都收费,这个明明可以在安装JDK的目录中找到啊!自己下一个JDK就可以得到。

    jdk8源码包

    该压缩包中放的是jdk8的源码包,把该资源导入eclipse中就可看到jdk的源码!

    关于jdk动态代理的源码剖析

    对jdk中的动态代理执行过程进行了详细跟踪,并反编译了动态代理调用自动生成的代理类,并对其进行了详细讲解。

    JDK8完整源码包

    在中文网上找了一圈没找到, 专门到openjdk下载了完整的源码包, 这份源码包含了java-fx, sun私有实现, 需要的可以下载.

    JDK8源码 注释附带中文翻译

    压缩包中为JDK8的源码,在源码的注释下方附带的中文翻译,是本压缩包的亮点,下方为局部代码,示范给大家: * Sole constructor. Programmers cannot invoke this constructor. * It is for use by code emitted ...

    java jdk 实例宝典源码

    java jdk 实例宝典源码 java jdk 实例宝典源码 java jdk 实例宝典源码

    jdk1.6 源码jdk1.6 源码

    jdk1.6 源码

    jdk 8 源码

    jdk 8 源码下载,jdk 8 源码下载,jdk 8 源码下载,jdk 8 源码下载

    jdk8官方源码包

    学习jdk源码时,用来比较各个版本的jdk的那些不同,jdk8增加啦函数式编程,算是比较大的改变 ,但jdk的向下兼容性非常强,入门级的可以看看jdk6,jdk7 jdk6是比较经典的jdk由此开始jee企业级开发,有兴趣同学可以去...

    jdk 1.8 源码

    jdk 1.8 源码,具体版本为jdk-8u131,向下兼容1.7和1.6。

    JDK1.8中文源码手册(无广告)

    JDK1.8中文源码手册 无广告

    JAVA8 完整源码(包含Sun包源码)jdk8u-src.7z

    的jdk8u/jdk8u/master分支。如果自己懒得弄,可以直接使用我的哦! 本来是用来让idea 查看sun包下源码。 具体操作方式:在idea的project structure设置面板的SDKs下,配置jdk的SourcePath,把解压的目录追加进去就...

    JDK动态代理源码

    JDK动态代理源码下载,动态产生代理,实现对【不同类】,【不同方法】的代理

    jdk6 源码 SRC

    jdk6 源码jdk6 源码jdk6 源码jdk6 源码jdk6 源码jdk6 源码

    jdk1.8 源码中文版,jdk直接显示中文注释

    下载后直接去本机jdk目录里替换jdk中的src.zip 再打开idea就能看到中文版的源码注释 示例 https://blog.csdn.net/a7459/article/details/106495622

    JDK1.7 API源码

    JDK1.7 API源码, 本人制作了一个JDK API官方源代码的chm,方便在移动设备上查看

    jdk8源码文件.zip

    jdk8的源码文件,解压压缩包,把src.zip文件放到jdk的安装目录下,idea的sourcepath添加src.zip即可

Global site tag (gtag.js) - Google Analytics