赞
踩
reentrantReadWriteloc 解决主要解决的是 读读共享,读写互斥。在读多写少的场景下,读写锁具有较高的性能。
它只允许读读共存,而读写和写写依然是互斥的,大多实际场景是“读/读”线程间并不存在互斥关系。只有“读/写”线程或者“写/写”线程间的操作需要互斥的,因此引入ReentrantReadWriteLock。
一个ReentrantReadWriteLock同时只能存在一个写锁,但是可以存在多个读锁,但不能同时存在写锁和读锁(切菜还是拍菜选一个),也即一个锁可以被多个读操作访问 或者 一个写操作访问,但两者不能同时进行。
只有在读多写少的场景之下,读写锁才具有较高的性能体现。
降级过程: 写锁变为读锁的过程。读锁不能升级为写锁。在ReentrantReadWriteLock中,当读锁在使用时,如果有线程尝试获取写锁,该写线程将会被阻塞,所以,需要释放读锁后,才可以获取写锁。
- package com.ljf.thread.readwritelock;
-
- import java.util.HashMap;
- import java.util.Map;
- import java.util.concurrent.locks.ReadWriteLock;
- import java.util.concurrent.locks.ReentrantLock;
- import java.util.concurrent.locks.ReentrantReadWriteLock;
-
- /**
- * @ClassName: MyResources
- * @Description: TODO
- * @Author: admin
- * @Date: 2023/05/21 11:33:08
- * @Version: V1.0
- **/
- public class MyResources {
- ReentrantLock mylock=new ReentrantLock();
- Map<String,String> resultMap=new HashMap<>();
- ReadWriteLock myrwLock=new ReentrantReadWriteLock();
- public void writeSomething(String key,String value){
- myrwLock.writeLock().lock();
- try {
- System.out.println("线程:"+Thread.currentThread().getName()+"正在写...");
- Thread.sleep(500);
- resultMap.put(key,value);
- System.out.println("线程:"+Thread.currentThread().getName()+"写入完成...");
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- finally {
- myrwLock.writeLock().unlock();
-
- }
- }
- public void readSomething(String key){
- myrwLock.readLock().lock();
- try {
- System.out.println("线程:"+Thread.currentThread().getName()+"正在读...");
- resultMap.get(key);
- Thread.sleep(200);
- System.out.println("线程:"+Thread.currentThread().getName()+"读取完成...");
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- finally {
- myrwLock.readLock() .unlock();
- }
- }
- }
- package com.ljf.thread.readwritelock;
-
- /**
- * Hello world!
- *
- */
- public class App
- {
- public static void main( String[] args )
- {
- System.out.println( "Hello World!" );
- MyResources myResources=new MyResources();
- for(int k=0;k<10;k++){
- final int m=k;
- new Thread(new Runnable() {
- @Override
- public void run() {
- myResources.writeSomething("do"+m,m+"");
- }
- }).start();
- }
- System.out.println("=====");
- for(int k=0;k<10;k++){
- final int m=k;
- new Thread(new Runnable() {
- @Override
- public void run() {
- myResources.readSomething("do"+m);
- }
- }).start();
- }
- }
- }
A:线程0-9 都是写线程,写线程互斥,线程一个写完,另外一个写;
B:线程10-19 线程都是读,读线程共享,多个线程可以同时读
说明: A内部之间,A和B之间都是 阻塞的,互斥的; B内部之间的读线程都是共享的
- Hello World!
- 线程:Thread-1正在写...
- =====
- 线程:Thread-1写入完成...
- 线程:Thread-3正在写...
- 线程:Thread-3写入完成...
- 线程:Thread-0正在写...
- 线程:Thread-0写入完成...
- 线程:Thread-2正在写...
- 线程:Thread-2写入完成...
- 线程:Thread-4正在写...
- 线程:Thread-4写入完成...
- 线程:Thread-5正在写...
- 线程:Thread-5写入完成...
- 线程:Thread-6正在写...
- 线程:Thread-6写入完成...
- 线程:Thread-7正在写...
- 线程:Thread-7写入完成...
- 线程:Thread-8正在写...
- 线程:Thread-8写入完成...
- 线程:Thread-9正在写...
- 线程:Thread-9写入完成...
- 线程:Thread-10正在读...
- 线程:Thread-11正在读...
- 线程:Thread-12正在读...
- 线程:Thread-13正在读...
- 线程:Thread-14正在读...
- 线程:Thread-15正在读...
- 线程:Thread-17正在读...
- 线程:Thread-18正在读...
- 线程:Thread-16正在读...
- 线程:Thread-19正在读...
- 线程:Thread-15读取完成...
- 线程:Thread-19读取完成...
- 线程:Thread-16读取完成...
- 线程:Thread-12读取完成...
- 线程:Thread-14读取完成...
- 线程:Thread-11读取完成...
- 线程:Thread-13读取完成...
- 线程:Thread-10读取完成...
- 线程:Thread-18读取完成...
- 线程:Thread-17读取完成...
- package com.ljf.thread.readwritelock;
-
- /**
- * Hello world!
- *
- */
- public class App
- {
- public static void main( String[] args )
- {
- System.out.println( "Hello World!" );
- MyResources myResources=new MyResources();
- for(int k=0;k<10;k++){
- final int m=k;
- new Thread(new Runnable() {
- @Override
- public void run() {
- myResources.writeSomething("do"+m,m+"");
- }
- }).start();
- }
- System.out.println("=====");
- for(int k=0;k<10;k++){
- final int m=k;
- new Thread(new Runnable() {
- @Override
- public void run() {
- myResources.readSomething("do"+m);
- }
- }).start();
- }
-
- for (int i = 1; i <=3; i++) {
- final int m=i;
- new Thread(new Runnable() {
- @Override
- public void run() {
- myResources.writeSomething("do"+m,m+"");
- }
- }).start();
- }
-
- }
-
-
- }
结果:
A:线程0-9 都是写线程,写线程互斥,线程一个写完,另外一个写;
B:线程10-19 线程都是读,读线程共享,多个线程可以同时读
C: 线程20-22 都是写线程,写线程互斥,线程一个写完,另外一个写;
说明: A内部之间,A和B之间都是 阻塞的,互斥的; B内部之间的读线程都是共享的
A内部之间,A和B,B和C ,C内部之间,读写互斥,
B内部之间读读共享,B和C,B读线程没有完成时,其它写线程C无法获取锁。
线程:Thread-0正在写...
=====
线程:Thread-0写入完成...
线程:Thread-1正在写...
线程:Thread-1写入完成...
线程:Thread-2正在写...
线程:Thread-2写入完成...
线程:Thread-3正在写...
线程:Thread-3写入完成...
线程:Thread-4正在写...
线程:Thread-4写入完成...
线程:Thread-5正在写...
线程:Thread-5写入完成...
线程:Thread-6正在写...
线程:Thread-6写入完成...
线程:Thread-7正在写...
线程:Thread-7写入完成...
线程:Thread-8正在写...
线程:Thread-8写入完成...
线程:Thread-9正在写...
线程:Thread-9写入完成...
线程:Thread-10正在读...
线程:Thread-11正在读...
线程:Thread-12正在读...
线程:Thread-13正在读...
线程:Thread-14正在读...
线程:Thread-16正在读...
线程:Thread-15正在读...
线程:Thread-17正在读...
线程:Thread-19正在读...
线程:Thread-18正在读...
线程:Thread-11读取完成...
线程:Thread-10读取完成...
线程:Thread-12读取完成...
线程:Thread-16读取完成...
线程:Thread-14读取完成...
线程:Thread-18读取完成...
线程:Thread-19读取完成...
线程:Thread-13读取完成...
线程:Thread-17读取完成...
线程:Thread-15读取完成...
线程:Thread-20正在写...
线程:Thread-20写入完成...
线程:Thread-21正在写...
线程:Thread-21写入完成...
线程:Thread-22正在写...
线程:Thread-22写入完成...
ReentrantReadWriteLock读锁被占用的时候,其他线程尝试获取写锁的时候被阻塞。但是,stampedLock采取乐观锁获取锁后,其他线程尝试获取写锁时不会被阻塞,这其实是对读锁的优化,所以,在获取乐观读锁后,还需要对结果进行校验。
所有获取锁的方法,都返回一个邮戳,stamp为零表示获取失败,其余都表示成功。
所有释放锁的方法,都需要一个邮戳,这个stamp必须是和成功获取锁时得到的stamp一致。
Stampedlock是不可重入的,危险(如果一个线程已经持有了写锁,再去获取写锁的话就会造成死锁)
Stampedlock的3种访问模式:
1.reading 读模式悲观,功能和ReentrantReadWriteLock的读锁类似。
2.writing 写模式:功能和ReentrantReadWriteLock的写锁类似。
3.optimistic reading 乐观锁模式: 无锁机制,类似数据库中的乐观锁,支持读写开发,很乐观认为读取时没人修改,加入被修改再实现升级为悲观读模式。
1.代码:
//暂停2秒钟线程
try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
- package com.ljf.thread.readwritelock.stamp;
-
- import java.util.concurrent.TimeUnit;
- import java.util.concurrent.locks.StampedLock;
-
- /**
- * @ClassName: StampLock
- * @Description: TODO
- * @Author: admin
- * @Date: 2023/10/01 21:26:47
- * @Version: V1.0
- **/
- public class StampLock {
- static int number = 37;
- static StampedLock stampedLock = new StampedLock();
-
- public static void main(String[] args) {
- StampLock resource=new StampLock();
-
- new Thread(() -> {
- resource.tryOptimisticRead();
- },"readThread").start();
-
- //暂停2秒钟线程,读过程可以写介入,演示
- //try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
-
- //暂停6秒钟线程
- try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
-
- new Thread(() -> {
- System.out.println(Thread.currentThread().getName()+"\t"+"----come in");
- resource.write();
- },"writeThread").start();
- }
- //乐观读,读的过程中也允许获取写锁介入
- public void tryOptimisticRead()
- {
- long stamp = stampedLock.tryOptimisticRead();
- int result = number;
- //故意间隔4秒钟,很乐观认为读取中没有其它线程修改过number值,具体靠判断
- System.out.println("4秒前stampedLock.validate方法值(true无修改,false有修改)"+"\t"+stampedLock.validate(stamp));
- for (int i = 0; i < 4; i++) {
- try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
- System.out.println(Thread.currentThread().getName()+"\t"+"正在读取... "+i+" 秒" +
- "后stampedLock.validate方法值(true无修改,false有修改)"+"\t"+stampedLock.validate(stamp));
- }
- if(!stampedLock.validate(stamp))
- {
- System.out.println("有人修改过------有写操作");
- stamp = stampedLock.readLock();
- try
- {
- System.out.println("从乐观读 升级为 悲观读");
- result = number;
- System.out.println("重新悲观读后result:"+result);
- }finally {
- stampedLock.unlockRead(stamp);
- }
- }
- System.out.println(Thread.currentThread().getName()+"\t"+" finally value: "+result);
- }
- public void write()
- {
- long stamp = stampedLock.writeLock();
- System.out.println(Thread.currentThread().getName()+"\t"+"写线程准备修改");
- try
- {
- number = number + 13;
- }finally {
- stampedLock.unlockWrite(stamp);
- }
- System.out.println(Thread.currentThread().getName()+"\t"+"写线程结束修改");
- }
- }
2.执行结果
3.代码:
//暂停6秒钟线程
try { TimeUnit.SECONDS.sleep(6); } catch (InterruptedException e) { e.printStackTrace(); }
- package com.ljf.thread.readwritelock.stamp;
-
- import java.util.concurrent.TimeUnit;
- import java.util.concurrent.locks.StampedLock;
-
- /**
- * @ClassName: StampLock
- * @Description: TODO
- * @Author: admin
- * @Date: 2023/10/01 21:26:47
- * @Version: V1.0
- **/
- public class StampLock {
- static int number = 37;
- static StampedLock stampedLock = new StampedLock();
-
- public static void main(String[] args) {
- StampLock resource=new StampLock();
-
- new Thread(() -> {
- resource.tryOptimisticRead();
- },"readThread").start();
-
- //暂停2秒钟线程,读过程可以写介入,演示
- //try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
-
- //暂停6秒钟线程
- try { TimeUnit.SECONDS.sleep(6); } catch (InterruptedException e) { e.printStackTrace(); }
-
- new Thread(() -> {
- System.out.println(Thread.currentThread().getName()+"\t"+"----come in");
- resource.write();
- },"writeThread").start();
- }
- //乐观读,读的过程中也允许获取写锁介入
- public void tryOptimisticRead()
- {
- long stamp = stampedLock.tryOptimisticRead();
- int result = number;
- //故意间隔4秒钟,很乐观认为读取中没有其它线程修改过number值,具体靠判断
- System.out.println("4秒前stampedLock.validate方法值(true无修改,false有修改)"+"\t"+stampedLock.validate(stamp));
- for (int i = 0; i < 4; i++) {
- try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
- System.out.println(Thread.currentThread().getName()+"\t"+"正在读取... "+i+" 秒" +
- "后stampedLock.validate方法值(true无修改,false有修改)"+"\t"+stampedLock.validate(stamp));
- }
- if(!stampedLock.validate(stamp))
- {
- System.out.println("有人修改过------有写操作");
- stamp = stampedLock.readLock();
- try
- {
- System.out.println("从乐观读 升级为 悲观读");
- result = number;
- System.out.println("重新悲观读后result:"+result);
- }finally {
- stampedLock.unlockRead(stamp);
- }
- }
- System.out.println(Thread.currentThread().getName()+"\t"+" finally value: "+result);
- }
- public void write()
- {
- long stamp = stampedLock.writeLock();
- System.out.println(Thread.currentThread().getName()+"\t"+"写线程准备修改");
- try
- {
- number = number + 13;
- }finally {
- stampedLock.unlockWrite(stamp);
- }
- System.out.println(Thread.currentThread().getName()+"\t"+"写线程结束修改");
- }
- }
4.结果
Stampedlock不支持重入,没有Re开头;stampedLock的悲观读锁和写锁不支持条件变量condition;使用stampedlock一定不要调用中断操作,即不要调用interrupt()方法。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。