读锁的调用,最终委派给其内部类 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. worker进程中线程的分类及用途

    worker进程中线程的分类及用途 欢迎转载,转载请注明出版,徽沪一郎. 本文重点分析storm的worker进程在正常启动之后有哪些类型的线程,针对每种类型的线程,剖析其用途及消息的接收与发送流程. ...

  2. 1 MySQL概述

    目录: 1. 简述 2. 历史 3. 同类产品 4. 优点和不足 5. MySQL存储引擎 6. MySQL架构 1. 简述 MySQL是一个关系型数据库管理系统.其体积小,速度快,开发源代码,使用成 ...

  3. SharePoint开发

    做SharePoint开发有时候是一件比较痛苦的事情,毕竟庞大的框架总是笨重的~~ 往往如果采取传统的方式开发SharePoint的话,更改一个代码需要有以下操作: 1)更改代码 2)VS编译——&g ...

  4. JS核心概念

    Javascript本质第一篇:核心概念   很多人在使用Javascript之前都至少使用过C++.C#或Java,面向对象的编程思想已经根深蒂固,恰好Javascript在语法上借鉴了Java,虽 ...

  5. ios学习笔记第三天之UI控件

  6. [置顶] 学习JDK源码:可进一步优化的代码

    1.参数化类型的构造函数比较啰嗦 new HashMap<String, List<String>>() 如果你调用参数化类的构造函数,那么很不幸,你必须要指定类型参数,即便上 ...

  7. jQuery Validation让验证变得如此容易(一)

    一.官网下载jquery,和jquery validation plugin http://jqueryvalidation.org/ 二.引入文件 <script src="js/j ...

  8. java遍历Set集合

    class Person{ private String name; private int age; public Person(String name,int age){ this.name = ...

  9. JSP实现页面跳转的方式

    js实现页面跳转的几种方式普通方式 第一种: <script language="javascript" type="text/javascript"&g ...

  10. Coding the Matrix作业Python Lab及提交方法

    Coding the Matrix: Linear Algebra through Computer Science Applications 这是一门用python实现矩阵运算的课,第一次作业就感觉 ...