读写锁案例 + 小小总结

//读这篇文章的时候,建议先看一下,我并发专题中的 Lock.lock() 加锁的过程的文章
//我在写读写锁的时候,好多东西,好多理念都在lock()中体现
//  读读串行
//  读写串行
//  写写串行import java.util.Random;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;public class ReadWriteLockDemo {ReadWriteLock readWriteLock = new ReentrantReadWriteLock();private Integer data = 0;public void get(){readWriteLock.readLock().lock();System.out.println(Thread.currentThread().getName() + "ready read");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "数据 = " + data);readWriteLock.readLock().unlock();}public void put(Integer data){readWriteLock.writeLock().lock();System.out.println(Thread.currentThread().getName() + "ready write");try {Thread.sleep(1000);this.data = data;} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "写入的数据 = " + data);readWriteLock.writeLock().unlock();}public static void main(String[] args) {final ReadWriteLockDemo readWriteLockDemo = new ReadWriteLockDemo();for (int i = 0; i < 10; i++) {new Thread(()->readWriteLockDemo.get()).start();new Thread(()->readWriteLockDemo.put(new Random().nextInt(10))).start();}}
}/**put这个操作 加了写锁,写锁是排他锁,加上写锁后,不能在加其它锁,加上写锁后,读锁加上后不能加其它任何锁get 读锁 可以允许很多读写进来的Thread-11ready writeThread-11写入的数据 = 8   这两个输出是不能分开的,write锁是排他锁Thread-2ready readThread-6ready readThread-4ready read读锁,这个是能分开的为什么会这样呢? 下面源码解释一下*/

其实,不管是lock也好,读写锁也罢,都是有一个状态state

这个state在lock中 0 初始状态 1、加锁状态 N(N > 1)重入锁

在读写锁中,也是有state这个状态的,但是,这个state是由32个bit位组成的,

0000 0000 0000 0000 0000 0000 0000 0000

前16个bit 读锁

后16个bit 写锁

ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
readWriteLock.readLock().lock();
System.out.println("rrrrrrrrrrrrrr");
readWriteLock.writeLock().lock();
System.out.println("wwwwwwwwwwwwww");//看到这个代码的时候,我有一个想法?
//首先,我是读锁,输出了 rrrrrrrrrrrrrr
//readWriteLock.writeLock().lock();  我能否升级一下呢?  把读锁升级为写锁  当然是 不能
//为什么锁不能升级?
//因为,读锁可以并发,如果,现在有两个线程,t1 t2两个线程现在都是读锁,要升级写锁
//t1在等待t2释放锁,t2在等t1释放锁,死锁了//读锁是共享锁,比如有100个线程同时占有这个锁,都在等待对方释放锁,无法保证100%释放锁。//一旦写锁成功上锁, 写锁是排它锁,state从0变成1,只有一个写线程ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
readWriteLock.writeLock().lock();
System.out.println("wwwwwwwwwwwww");
readWriteLock.readLock().lock();
System.out.println("rrrrrrrrrrrrr");//那,既然锁升级不能行,那锁降级呢?  其实是可以的
//锁降级就可以了,因为写锁是排他锁,可以保证让读100%占用到锁。
//锁降级,既然写锁是排它锁,为什么这个读锁不能等写锁释放再拿?
//因为,它一旦释放了锁,瞬间就会有很多线程来抢占这个state,你能否抢到就不一定了

写锁加锁过程

//这边和Lock.lock() 是一模一样的
public final void acquire(int arg) {//tryAcquire(arg) 尝试加锁失败,和Lock.lock()一模一样,初始化同步队列if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))selfInterrupt();
}

static final int SHARED_SHIFT   = 16;
//1 << SHARED_SHIFT           1 0000 0000 0000 0000
//EXCLUSIVE_MASK            0 1111 1111 1111 1111
static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
static final int MAX_COUNT      = (1 << SHARED_SHIFT) - 1;static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK;
}protected final boolean tryAcquire(int acquires) {Thread current = Thread.currentThread();    //拿到当前线程//第一个线程进来,c肯定为0int c = getState();//把写的状态拿出来int w = exclusiveCount(c);//c != 0 证明已经上锁成功了  可能是写锁 也可能是读锁if(c != 0){//w == 0 证明加的不是写锁//current != getExclusiveOwnerThread()  不是重入锁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;}//writerShouldBlock()  这个判断是  是否需要排队//如果,不需要排队,看看能否加锁成功if (writerShouldBlock() || !compareAndSetState(c, c + acquires)){return false;}//这边肯定是已经加锁成功了,设置当前线程setExclusiveOwnerThread(current);return true;
}//这个和lock() 里面的那个东西是一模一样的
//就是看看 时候需要等待
public final boolean hasQueuedPredecessors() {Node t = tail;Node h = head;Node s;return h != t && ((s = h.next) == null || s.thread != Thread.currentThread());
}

读锁加锁过程

private transient Thread firstReader = null;
private transient int firstReaderHoldCount;
private transient HoldCounter cachedHoldCounter;
private transient ThreadLocalHoldCounter readHolds;
static final int SHARED_SHIFT   = 16;static int sharedCount(int c) { return c >>> SHARED_SHIFT;
}static final class HoldCounter {int count = 0;final long tid = getThreadId(Thread.currentThread());
}public final void acquireShared(int arg) {if (tryAcquireShared(arg) < 0){doAcquireShared(arg);}
}protected final int tryAcquireShared(int unused) {Thread current = Thread.currentThread();int c = getState();//exclusiveCount(c) != 0  这个是用来判断是否有写锁的 != 0 证明是有写锁的//getExclusiveOwnerThread() != current 但是加锁线程不是当前线程//因此,有写锁了,但是进来的又不是那个线程(不是重入锁),加锁失败,返回 -1if (exclusiveCount(c) != 0 && getExclusiveOwnerThread() != current){return -1;}//如果还没有加锁,c为0 右移16位,r为0//如果已经加锁了,此时肯定是读锁,c不为0,右移16位,r不为0//readerShouldBlock() 读锁是共享锁,为什么还要去判断有没有等待队列?//就是为了防止,此时,有写锁在排队//r < MAX_COUNT  不能达到最大值//compareAndSetState(c, c + SHARED_UNIT) // static final int SHARED_UNIT    = (1 << SHARED_SHIFT);int r = sharedCount(c);if (!readerShouldBlock() && r < MAX_COUNT && compareAndSetState(c, c + SHARED_UNIT)) {//r == 0 证明还没有加锁过,不管是读锁 还是 写锁  都没有//firstReader == current 这个严格意义上来说不是重入锁,只算是firstReader的次数(下面图片解释)//这边的步骤,建议你配置着下面的图,进行逻辑分析一下。//第一个线程(读锁)就是上面的那个//那其他的呢?HoldCounter rh = cachedHoldCounter;  这个HoldCounter里面维护了一个countif (r == 0) {firstReader = current;firstReaderHoldCount = 1;} else if (firstReader == current) {firstReaderHoldCount++;}else {//其他线程,整了一个缓存数据结构  cachedHoldCounterHoldCounter rh = cachedHoldCounter;if (rh == null || rh.tid != getThreadId(current)){//为当前线程初始化一个rh//有个疑问?那么多线程,怎么区分呢? ThreadLocalcachedHoldCounter = rh = readHolds.get();//当前线程已经被初始化对象了//count + 1 设置 rh对象}else if (rh.count == 0){readHolds.set(rh);}rh.count++;}return 1;}return fullTryAcquireShared(current);
}

​ 上面的图解释了,为什么上面说的 那个 严格意义上来说 不是重入锁?

​ 在Lock.lock() 源码中,如果c为 > 1 就是锁的重入,这个读写锁就不一定是了。

​ 读锁是共享锁,每次来一个读锁,都会把 r + 1 的 ,因此,这个r就是个总和。

​ 如果说,想看看哪个线程是重入的,就是自己线程维护的 ReaderHoldCount 这个值就行了。

ReadWriteLock读写锁加锁过程相关推荐

  1. java多线程 -- ReadWriteLock 读写锁

    写一条线程,读多条线程能够提升效率. 写写/读写 需要"互斥"; 读读 不需要互斥. ReadWriteLock 维护了一对相关的锁,一个用于只读操作,另一个用于写入操作.只要没有 ...

  2. 【Java锁体系】ReadWriteLock读写锁是什么?什么是读写锁

    [Java锁体系]ReadWriteLock读写锁场景 一.背景 像我们所知的ReentrantLock.synchronized关键字都是排它锁,这些锁在同一时刻只允许一个线程访问. 而读写锁允许在 ...

  3. ReadWriteLock读写锁的使用

    读写锁: 写写.读写 是互斥的 读读是不需要互斥的 如下示例代码中,可以看到读的操作,一部分是在写操作之前,一部分是在写操作之后,说明读是不互斥的.写是 示例代码: package com.debug ...

  4. GUC-9 ReadWriteLock : 读写锁

    import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWrit ...

  5. Java并发编程-ReadWriteLock读写锁

    1.ReadWriteLock介绍 为什么我们有了Lock,还要用ReadWriteLock呢.我们对共享资源加锁之后,所有的线程都将会等待.Lock读操作也锁,写操作也会锁,而对共享资源读的时候,其 ...

  6. java writelock 问题_【转】java并发编程系列之ReadWriteLock读写锁的使用

    前面我们讲解了Lock的使用,下面我们来讲解一下ReadWriteLock锁的使用,顾明思义,读写锁在读的时候,上读锁,在写的时候,上写锁,这样就很巧妙的解决synchronized的一个性能问题:读 ...

  7. java 可重入读写锁 ReentrantReadWriteLock 详解

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt206 读写锁 ReadWriteLock读写锁维护了一对相关的锁,一个用于只 ...

  8. java 单例 读写锁_终极锁实战:单JVM锁+分布式锁

    目录 1.前言 2.单JVM锁 3.分布式锁 4.总结 =========正文分割线================= 1.前言 锁就像一把钥匙,需要加锁的代码就像一个房间.出现互斥操作的典型场景:多 ...

  9. Windows平台下的读写锁

    Windows平台下的读写锁 简单介绍Windows平台下的读写锁以及实现. 背景介绍 Windows在Vista 和 Server2008以后才开始提供读写锁API,即SRW系列函数(Initial ...

最新文章

  1. 十、最大熵模型与EM算法
  2. 服务器系统玩dnf,win7系统玩dnf提示正在连接服务器的解决方法
  3. CUBA平台–新的Java企业应用程序框架
  4. java 监听器 分类_java过滤器和监听器详解 分类: 学习专区
  5. 最近总结了串口(COM)读写操作的三种方式
  6. 苹果6s最大屏幕尺寸_iPhone12来了,我决定给老苹果升级一下电池_电池
  7. onvif学习笔记6:onvif的OSD坐标小记
  8. 牛客网暑期ACM多校训练营7: C. Bit Compression(DFS+预处理)
  9. Anacond win10安装与介绍
  10. 游戏制作大致流程粗谈之五
  11. vue引入百度离线地图
  12. 概率dp(A - Scout YYF I POJ - 3744 )
  13. Egret和LayaBox
  14. 伊斯兰教历的计算和各个月的名称
  15. Meltdown Reading Kernel Memory from User Space
  16. 和导师闹僵跑来实习?拼了命也要拿到大厂实习offer
  17. 多元线性模型中共线性产生的原因解析
  18. 【博客表情包】emoji和符号表情包
  19. MySQL的查询及删除重复记录
  20. R语言LR逻辑回归实例

热门文章

  1. Linux监控操作系统CPU、内存、磁盘、网络和dstat
  2. 微信小程序-云数据库开发
  3. Python库的安装详解
  4. 丘成桐计算机竞赛用什么电脑,丘成桐这样的学术竞赛到底对申请有没有用?
  5. 似然估计 Hessain Fisher Information
  6. mysql同时查两张表数据库表_sql语句 同时查询两个表
  7. 读行学区块链专栏 | 全球区块链资讯Top10
  8. 零基础如何入门编程开发?
  9. Ubuntu-第一次安装Ubuntu在笔记本及一些远程软件的安装
  10. PostGIS查询指定范围的数据