import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.springframework.ui.context.Theme;
public class ReentrantReadWriteLockTest {
// 场景:现实中有这样一种场景:对共享资源有读和写的操作,且写操作没有读操作那么频繁。在没有写操作的时候,多个线程同时读一个资源没有任何问题,
// 所以应该允许多个线程同时读取共享资源;但是如果一个线程想去写这些共享资源,就不应该允许其他线程对该资源进行读和写的操作了
// 可重入读写锁 读写锁确保写锁的操作对读锁可见。
/**
* 1;含义:他表示 两个锁,一个是读锁也叫共享锁,一个是写锁,也叫排他锁
* 2: 特性: 1.1: 锁可重入 1.2:读写分离 1.3:可降级
* 锁降级含义 :锁降级指的是写锁降级为读锁
* 因为读锁与读锁之间不互斥,如果是写锁与读锁或者是写锁与写锁就会互斥,所以由写锁变为读锁就降级了
* 如果当前线程拥有写锁,然后将其释放,最后再获取读锁,这种并不能称之为锁降级。
* 锁降级指的是把持住(当前拥有的)写锁,再获取到读锁,随后释放(先前有用的)写锁的过程。
*
*/
private static ReentrantReadWriteLock rw = new ReentrantReadWriteLock();
/**
* volatile 保证了变量的可见性,被volatile 修饰的变量,如果有值变更,其他线程立马可见
* 注: volatile只能保证变量的可见性,不能保证对volatile变量操作的原子性
*/
private volatile static Map<String,Object> map = new HashMap<String, Object>();
// 判断是否发生了 修改行为
private volatile static Boolean isUpdate =false;
private volatile static int num=0;
public static void main(String[] args) {
// TODO Auto-generated method stub
for(int i=0;i<=10;i++) {
final int tempInt= i;
new Thread(()-> {
try {
put(tempInt+"",tempInt+"");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
},String.valueOf(i) ).start();;
}
for(int i=0;i<=10;i++) {
final int tempInt= i;
new Thread( ()->{
try {
get(tempInt+"");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}).start();
}
for(int i=30;i<=40;i++) {
// final int tempInt= i;
new Thread( ()->{
processCacheData();
},i+"").start();
}
}
public static void put(String key ,Object value) throws InterruptedException {
// 加写锁
rw.writeLock().lock();
System.out.println(Thread.currentThread().getName() + "正在写入" + key);
// 休眠3 秒
TimeUnit.MILLISECONDS.sleep(300);
map.put(key, value);
System.out.println(Thread.currentThread().getName() +" 写入" + key);
// 释放写锁
rw.writeLock().unlock();
}
public static void get(String key ) throws InterruptedException {
// 加读锁
rw.readLock().lock();
System.out.println(Thread.currentThread().getName() + " 正在读取" + key);
TimeUnit.MILLISECONDS.sleep(300);
Object result = map.get(key);
System.out.println(Thread.currentThread().getName()+"\t 读取完成:"+result);
rw.readLock().unlock();
}
/**
* 锁降级
*/
public static void processCacheData() {
//先获取读锁
rw.readLock().lock();
if(!isUpdate) {
//在释放读锁
rw.readLock().unlock();
// 在获取写锁
rw.writeLock().lock();
// 进行写操作的时候在isUpdate 的最后值
try {
if(!isUpdate) {
num++;
isUpdate=true;
System.out.println(Thread.currentThread().getName() +"正在进行写操作,写入后值为" +num);
//当前线程在获取 读锁
rw.readLock().lock();
System.out.println(Thread.currentThread().getName() +"我由写锁降低为读锁");
}
} finally {
System.out.println(Thread.currentThread().getName() +"我是读锁" + num);
// 释放掉写锁
rw.writeLock().unlock();
}
}
try{
//模拟5秒的处理时间,并打印出当前值,在这个过程中cacheValid可能被其他线程修改,锁降级保证其他线程写锁被阻塞,数据不被改变
TimeUnit.MILLISECONDS.sleep(300);
System.out.println(Thread.currentThread().getName() + ": " + num);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 当前线程保持的读取锁数量;如果当前线程从未保持过读取锁,则返回 0
if(rw.getReadHoldCount() > 0){
System.out.println("我释放掉了读锁" + Thread.currentThread().getName());
rw.readLock().unlock();
}
}
}
}