ReadWriteLock读写锁加锁过程
读写锁案例 + 小小总结
//读这篇文章的时候,建议先看一下,我并发专题中的 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读写锁加锁过程相关推荐
- java多线程 -- ReadWriteLock 读写锁
写一条线程,读多条线程能够提升效率. 写写/读写 需要"互斥"; 读读 不需要互斥. ReadWriteLock 维护了一对相关的锁,一个用于只读操作,另一个用于写入操作.只要没有 ...
- 【Java锁体系】ReadWriteLock读写锁是什么?什么是读写锁
[Java锁体系]ReadWriteLock读写锁场景 一.背景 像我们所知的ReentrantLock.synchronized关键字都是排它锁,这些锁在同一时刻只允许一个线程访问. 而读写锁允许在 ...
- ReadWriteLock读写锁的使用
读写锁: 写写.读写 是互斥的 读读是不需要互斥的 如下示例代码中,可以看到读的操作,一部分是在写操作之前,一部分是在写操作之后,说明读是不互斥的.写是 示例代码: package com.debug ...
- GUC-9 ReadWriteLock : 读写锁
import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWrit ...
- Java并发编程-ReadWriteLock读写锁
1.ReadWriteLock介绍 为什么我们有了Lock,还要用ReadWriteLock呢.我们对共享资源加锁之后,所有的线程都将会等待.Lock读操作也锁,写操作也会锁,而对共享资源读的时候,其 ...
- java writelock 问题_【转】java并发编程系列之ReadWriteLock读写锁的使用
前面我们讲解了Lock的使用,下面我们来讲解一下ReadWriteLock锁的使用,顾明思义,读写锁在读的时候,上读锁,在写的时候,上写锁,这样就很巧妙的解决synchronized的一个性能问题:读 ...
- java 可重入读写锁 ReentrantReadWriteLock 详解
详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt206 读写锁 ReadWriteLock读写锁维护了一对相关的锁,一个用于只 ...
- java 单例 读写锁_终极锁实战:单JVM锁+分布式锁
目录 1.前言 2.单JVM锁 3.分布式锁 4.总结 =========正文分割线================= 1.前言 锁就像一把钥匙,需要加锁的代码就像一个房间.出现互斥操作的典型场景:多 ...
- Windows平台下的读写锁
Windows平台下的读写锁 简单介绍Windows平台下的读写锁以及实现. 背景介绍 Windows在Vista 和 Server2008以后才开始提供读写锁API,即SRW系列函数(Initial ...
最新文章
- 十、最大熵模型与EM算法
- 服务器系统玩dnf,win7系统玩dnf提示正在连接服务器的解决方法
- CUBA平台–新的Java企业应用程序框架
- java 监听器 分类_java过滤器和监听器详解 分类: 学习专区
- 最近总结了串口(COM)读写操作的三种方式
- 苹果6s最大屏幕尺寸_iPhone12来了,我决定给老苹果升级一下电池_电池
- onvif学习笔记6:onvif的OSD坐标小记
- 牛客网暑期ACM多校训练营7: C. Bit Compression(DFS+预处理)
- Anacond win10安装与介绍
- 游戏制作大致流程粗谈之五
- vue引入百度离线地图
- 概率dp(A - Scout YYF I POJ - 3744 )
- Egret和LayaBox
- 伊斯兰教历的计算和各个月的名称
- Meltdown Reading Kernel Memory from User Space
- 和导师闹僵跑来实习?拼了命也要拿到大厂实习offer
- 多元线性模型中共线性产生的原因解析
- 【博客表情包】emoji和符号表情包
- MySQL的查询及删除重复记录
- R语言LR逻辑回归实例