读锁的调用,最终委派给其内部类 Sync extends AbstractQueuedSynchronizer
 /**
* 获取读锁,如果写锁不是由其他线程持有,则获取并立即返回;
* 如果写锁被其他线程持有,阻塞,直到读锁被获得。
*/
public void lock() {
sync.acquireShared(1);
}
/**
* 以共享模式获取对象,忽略中断。通过至少先调用一次 tryAcquireShared(int) 来实现此方法,并在成功时返回。
* 否则将线程加入队列,在阻塞和运行之间切换,重复调用tryAcquireShared直到成功
*( possibly repeatedly blocking and unblocking, invoking tryAcquireShared until success)
*/
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
tryAcquireShared方法的逻辑:
整体思路如下,具体实现细节,请参考下面的源码分析

一 写锁被占用的情况

   1 写锁被其他线程占用,获取失败

   2 写锁被自己占用,表示“锁降级”,进入【循环cas取读锁】流程

二 写锁没有被占用的情况   
    1 根据读锁获取策略判断是否阻塞(readerShouldBlock方法)

        (1) 公平锁策略:如果当前线程不是同步队列中的第一个节点,则阻塞

        (2) 非公平锁策略:为了防止写线程饥饿,如果同步队列中的第一个节点是写线程,则阻塞当前线程。

    2 如果需要阻塞,分以下2种情况:

         (1)当前线程是读锁重入,则不需要阻塞,进入【循环cas取读锁】流程

         (2)当前线程不是读锁重入,则获取失败

3 如果不需要阻塞,进入【循环cas取读锁】流程
三  【循环cas取读锁】

    1 tryAcquireShared方法先进行了一次cas取锁操作,如果获取失败,则调用fullTryAcquireShared方法,循环获取。

    2 什么是cas?请参考CAS
 

protected final int tryAcquireShared(int unused) {

            Thread current = Thread.currentThread();
//c是锁状态为:高位16位表示共享锁的数量,低位16位表示独占锁的数量
int c = getState();
//exclusiveCount(c) 取低16位的值,也就是写锁状态位:不等于0表示写锁被占用
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)
//写锁被其他线程占用,获取读锁失败
return -1; //取高16位的值,读锁状态位
int r = sharedCount(c);
//readerShouldBlock()根据读锁获取策略,返回是否阻塞当前读锁获取操作。后面会详细说明此方法
if (!readerShouldBlock() &&
r < MAX_COUNT &&
//cas修改高16位的读锁状态,即获取读锁
compareAndSetState(c, c + SHARED_UNIT)) { //首次获取读锁
if (r == 0) {
//缓存首次获取读锁的线程,及其读锁重入次数
firstReader = current;
firstReaderHoldCount = 1; } else if (firstReader == current) {
firstReaderHoldCount++; } else {
//cachedHoldCounter是最后获取锁的线程的读锁重入次数
HoldCounter rh = cachedHoldCounter; if (rh == null || rh.tid != getThreadId(current))
//readHolds是缓存了当前线程的读锁重入次数的ThreadLocal
//当前线程自然是最后获取锁的线程,故将当前线程的holdCounter赋给cachedHoldCounter
cachedHoldCounter = rh = readHolds.get();
else if (rh.count == 0)
//缓存当前线程的holdCounter
//fullTryAcquireShared()方法中,
//获取读锁失败的线程会执行:readHolds.remove(),故此时需要重新设置
readHolds.set(rh); rh.count++;
}
return 1;
}
//首次获取读锁失败后,重试获取
return fullTryAcquireShared(current);
}
   /**
* Full version of acquire for reads, that handles CAS misses
* and reentrant reads not dealt with in tryAcquireShared.
*/
final int fullTryAcquireShared(Thread current) {
//rh表示当前线程的锁计数器
HoldCounter rh = null;
for (;;) {
int c = getState(); //写锁被占用
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 = cachedHoldCounter;
if (rh == null || rh.tid != current.getId()) {
rh = readHolds.get();
if (rh.count == 0)
//线程阻塞之前,清空readHolds
readHolds.remove();
}
}
if (rh.count == 0)
//当前线程的锁计数器为0,非重入锁,需要阻塞。
return -1;
}
}
if (sharedCount(c) == MAX_COUNT)
throw new Error("Maximum lock count exceeded"); //cas设置读锁状态位
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 != current.getId())
rh = readHolds.get();
else if (rh.count == 0)
//锁计数器放入ThreadLocal
readHolds.set(rh); rh.count++;
//此时rh就是最后获取读锁的线程的锁计数器
cachedHoldCounter = rh; // cache for release
}
return 1;
}
}
}
  /**
* 非公平锁的读锁获取策略
*/
final boolean readerShouldBlock() {
//为了防止写线程饥饿
//如果同步队列中的第一个线程是以独占模式获取锁(写锁),
//那么当前获取读锁的线程需要阻塞,让队列中的第一个线程先执行
return apparentlyFirstQueuedIsExclusive();
} /**
* 公平锁的读锁获取策略
*/
final boolean readerShouldBlock() {
//如果当前线程不是同步队列头结点的next节点(head.next)
//则阻塞当前线程
return hasQueuedPredecessors();
}

java读写锁源码分析(ReentrantReadWriteLock)的更多相关文章

  1. java并发锁ReentrantReadWriteLock读写锁源码分析

    1.ReentrantReadWriterLock 基础 所谓读写锁,是对访问资源共享锁和排斥锁,一般的重入性语义为如果对资源加了写锁,其他线程无法再获得写锁与读锁,但是持有写锁的线程,可以对资源加读 ...

  2. 多线程高并发编程(4) -- ReentrantReadWriteLock读写锁源码分析

    背景: ReentrantReadWriteLock把锁进行了细化,分为了写锁和读锁,即独占锁和共享锁.独占锁即当前所有线程只有一个可以成功获取到锁对资源进行修改操作,共享锁是可以一起对资源信息进行查 ...

  3. 细说并发5:Java 阻塞队列源码分析(下)

    上一篇 细说并发4:Java 阻塞队列源码分析(上) 我们了解了 ArrayBlockingQueue, LinkedBlockingQueue 和 PriorityBlockingQueue,这篇文 ...

  4. Java split方法源码分析

    Java split方法源码分析 public String[] split(CharSequence input [, int limit]) { int index = 0; // 指针 bool ...

  5. 【JAVA】ThreadLocal源码分析

    ThreadLocal内部是用一张哈希表来存储: static class ThreadLocalMap { static class Entry extends WeakReference<T ...

  6. 【Java】HashMap源码分析——常用方法详解

    上一篇介绍了HashMap的基本概念,这一篇着重介绍HasHMap中的一些常用方法:put()get()**resize()** 首先介绍resize()这个方法,在我看来这是HashMap中一个非常 ...

  7. 【Java】HashMap源码分析——基本概念

    在JDK1.8后,对HashMap源码进行了更改,引入了红黑树.在这之前,HashMap实际上就是就是数组+链表的结构,由于HashMap是一张哈希表,其会产生哈希冲突,为了解决哈希冲突,HashMa ...

  8. Java并发包源码分析

    并发是一种能并行运行多个程序或并行运行一个程序中多个部分的能力.如果程序中一个耗时的任务能以异步或并行的方式运行,那么整个程序的吞吐量和可交互性将大大改善.现代的PC都有多个CPU或一个CPU中有多个 ...

  9. Java - "JUC" Semaphore源码分析

    Java多线程系列--“JUC锁”11之 Semaphore信号量的原理和示例 Semaphore简介 Semaphore是一个计数信号量,它的本质是一个"共享锁". 信号量维护了 ...

随机推荐

  1. 朴素贝页斯分类法 c++实现

    朴素贝叶斯法是基于贝叶斯定理与特征条件独立假设的分类方法.对于搞机器学习的同学们来说,这是相对简单但效果较好的模型. 朴素贝叶斯方法的理论 设输入为n维特征向量X={x1,x2,...,xn},输出为 ...

  2. T4 Template Overview

    T4 Template Overview   T4 Template的组成 指令区:为模板转换引擎提供指令,控制模板如何被处理 Ÿ   template:模板相关的属性,debug是否可以调试:hos ...

  3. Excel开发

    浅谈Excel开发:九 Excel 开发中遇到的常见问题及解决方法   Excel开发过程中有时候会遇到各种奇怪的问题,下面就列出一些本人在开发中遇到的一些比较典型的问题,并给出了解决方法,希望对大家 ...

  4. MongoDB:利用官方驱动改装为EF代码风格的MongoDB.Repository框架 二

    本次改动的主要内容是实现MongoDB.Repository对MongoDBRef的支持. MongoDB对一对一,一对多,多对多关系的维护,官方推荐文档嵌入方式,反映到模型的设计如下: public ...

  5. Paint与Canvas的简单用法

    参考:http://www.eoeandroid.com/forum.php?mod=viewthread&tid=278237 自定义View 重写onDraw方法 package com. ...

  6. BDD

    Binding business requirements to .NET code http://www.specflow.org/ 行为驱动开发 BDD:Behavior Driven Devel ...

  7. C#:.net/方法/字符串/数组

    C#:.net/方法/字符串/数组,那点事 首先还是先说下(几个概念的东西)c#下的.net平台的构造快及其功能作用和程序集: .net: .net平台是由:a:运行库+b:全面基础类库(这个是从程序 ...

  8. Java 多线程系列

    要编写线程安全的代码,其核心在于要对状态访问操作进行管理,特别是对共享的(Shared)和可变的(Mutable)状态的访问. Java中的主要同步机制是关键字synchronized,它提供了一种独 ...

  9. 自用类库整理之SqlHelper和MySqlHelper

    自用类库整理之SqlHelper和MySqlHelper 自用的SQLHelper和MySqlHelper,除一些通用方法外,封装了一些很实用的批量操作方法,简单介绍下 SqlHelper Execu ...

  10. Android 手机进入不了fastboot模式的解决方案

    本方案仅针对linux terminal下刷手机img文件的情况: fastboot的通常流程如下:   adb reboot bootloader  //进入bootloader 模式  fastb ...