目录
2. ReentrantReadWriteLock源码分析(TODO)
1.ReentrantReadWriteLock介绍
应用场景:读读操作不存在并发问题,读写,写写,写读场景下需要互斥,需要一把锁锁住需要的资源。
针对上述场景java实现了reentrantReadWriteLock,并且在java中实现了锁降级(写锁到读锁),公平与非公平选择,可重入。
设计一把锁如何实现:继承AQS,实现抽象方法tryRelease和tryAcquire方法,其他方法可以使用AQS已经封装好的。
管程:synchronized
AQS:cas+同步队列(获取锁失败阻塞)
设计一把读写锁如何实现:读读,读写,写读,写写锁设计
读锁:共享锁
写锁:独占锁
AQS中通过state中0->1表示获取到锁,读写锁中可以将int修饰的32位切分,高16位表示读锁,低16位表示写锁。
如何判断读锁和写锁:高16位不为0或低16位不为0
1.1 读读,读写,写读,写写实战
除了读读不互斥以外,其他三种都互斥,只有释放第一个锁才能获取下一个锁,演示代码如下。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteLockTest {
//定义一个读写锁,并且定义一个读锁,一个写锁
private ReadWriteLock lock = new ReentrantReadWriteLock();
private Lock r = lock.readLock();
private Lock w = lock.writeLock();
//定义一个对象
private Object data = "";
//定义一个读方法
public void read() {
System.out.println(Thread.currentThread().getName()+"读锁-begin");
r.lock();
try {
System.out.println(Thread.currentThread().getName()+"获取读锁");
data = "read";
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
r.unlock();
System.out.println(Thread.currentThread().getName()+"读锁-解锁");
}
}
//定义一个写方法
public void write() {
System.out.println(Thread.currentThread().getName()+"写锁-begin");
w.lock();
try {
System.out.println(Thread.currentThread().getName()+"获取写锁");
data = "write";
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
w.unlock();
System.out.println(Thread.currentThread().getName()+"写锁-解锁");
}
}
public static void main(String[] args) {
//测试读读,读写,写读,写写场景
ReadWriteLockTest readWriteLockTest = new ReadWriteLockTest();
new Thread(()->{
// readWriteLockTest.read();
readWriteLockTest.write();
}).start();
new Thread(()->{
readWriteLockTest.read();
// readWriteLockTest.write();
}).start();
}
}
1.2 hashmap线程安全问题解决实战
使用ReadWriteLock对hashMap进行再一次的封装,可以实现并发访问安全问题。这种场景适合读多写少。
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteLockTest2 {
//定义一个hashMap和读写锁
private static Map<String,Object> map = new HashMap<String,Object>();
private static ReadWriteLock lock = new ReentrantReadWriteLock();
private static Lock r = lock.readLock();
private static Lock w = lock.writeLock();
//获取时获取读锁
public static Object get(String key){
try{
r.lock();
return map.get(key);
}finally {
r.unlock();
}
}
//插入时插入写锁
public static void put(String key,Object value){
try{
w.lock();
map.put(key,value);
}finally {
w.unlock();
}
}
//清理写锁
public void clear(){
try{
w.lock();
map.clear();
}finally {
w.unlock();
}
}
public static void main(String[] args) {
ReadWriteLockTest2.put("key","value");
System.out.println(ReadWriteLockTest2.get("key"));
}
}
1.3 锁降级实战
锁降级过程:写锁转为读锁,锁降级过程中读锁的获取主要是为了保证可见性。
应用场景不明
public class ReadWriteLockTest3 {
private static ReadWriteLock lock = new ReentrantReadWriteLock();
private static Lock r = lock.readLock();
private static Lock w = lock.writeLock();
private static boolean flag = false;
//锁降级过程:写锁降级为读锁,1.加读锁释放读锁 2.获取写锁中间加写锁释放写锁 3.try-finally释放读锁
public static void update(){
r.lock();
if(!flag){
r.unlock();
w.lock();//锁降级过程从此时开始
try{
if(!flag){
flag = true;
}
r.lock();
}finally {
w.unlock();//锁降级完成,写锁转为读锁
}
}
try{
//锁降级完成,写锁降为读锁
}finally {
r.unlock();
}
}
}
2. ReentrantReadWriteLock源码分析(TODO)
ReentrantReadWriteLock中HoldCounter和ThreadLocalHoldCounter是一个计数器。用这两个接口来存储获取读锁的个数。可重入次数存在在ThreadLocal中
关注点:
1. state如何存储读锁和写锁的状态
共享锁:int值往右移16位(2 >>> 16)
独占锁:位运算(int c & 65536)
2.读锁加解锁
加锁大致分为:
1.尝试获取锁判断是写锁,返回-1;读锁计数器HoldCounter+1
2. 入队操作,创建队列,获取锁失败进入同步队列阻塞队列
解锁:获取锁状态值判断将计数器使用CAS操作--操作
3.写锁加解锁(TODO)