ReentrantReadWriteLock样例
public class ReadWriteLockTest {
public DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm:ss:SSS");
public ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
public String getTime(){
return LocalDateTime.now(ZoneOffset.of("+8")).format(formatter);
//字符串转时间String dateTimeStr = "2018-07-28 14:11:15";
}
public void readLockTest(){
try {
readWriteLock.readLock().lock();
System.out.println("start readLockTest at "+getTime());
Thread.sleep(2000);
System.err.println("end readLockTest at "+getTime());
}catch (InterruptedException e){
e.printStackTrace();
}finally {
readWriteLock.readLock().unlock();
}
}
public void writeLockTest(){
try {
readWriteLock.writeLock().lock();
System.out.println("start writeLockTest at "+getTime());
Thread.sleep(2000);
System.err.println("end writeLockTest at "+getTime());
}catch (InterruptedException e){
e.printStackTrace();
}finally {
readWriteLock.writeLock().unlock();
}
}
public static void main(String[] args) {
/*
a << b就表示把a转为二进制后左移b位(在后面添b个0)。
a >> b表示二进制右移b位(去掉末b位).
*/
ReadWriteLockTest readWriteLockTest = new ReadWriteLockTest();
IntStream.range(0,4).forEach(i-> new Thread(readWriteLockTest::readLockTest).start());
IntStream.range(0,4).forEach(i-> new Thread(readWriteLockTest::writeLockTest).start());
}
}
不厌其烦的阅读jdk源码
ReadWriteLock接口
其中一个实现类ReentrantReadWriteLock
构造方法和成员变量
WriteLock的构造类似,不再赘述
AQS在ReentrantReadWriteLock中的角色
ReentrantReadWriteLock的ReadLock获取锁的源码分析
ReentrantReadWriteLock的WriteLock获取锁的源码分析
Sync有两个版本
***ShouldBlock(),在获取锁的tryAcquire()方法中有调用
小结——关于ReentrantReadWriteLock获取锁的操作逻辑:
- 读锁:
- 在获取读锁时,会尝试判断当前对象是否拥有了写锁,如果已经拥有,则直接获取失败。
- 如果没有写锁,就表示当前对象没有排他锁(写锁),则当前线程会尝试给对象加锁。
- 如果当前线程已经持有了该对象的锁,那么直接将读锁数量加1(可重入)。
- 写锁:
- 在获取写锁时,会尝试判断当前对象是否拥有了锁(读锁与写锁),只能选其一。如果已经拥有且持有锁的线程并非当前线程,直接获取失败。
- 如果当前对象没有被加锁,那么写锁就会为当前对象上锁,并且将写锁的个数加1(可重入)。
- 将当前对象的排他锁线程持有者设为自己(
setExclusiveOwnerThread(current));
ReentrantReadWriteLock的ReadLock释放锁的源码分析
ReentrantReadWriteLock的WriteLock释放锁的源码分析
关于公平锁和非公平锁
- 公平锁就是严格的FIFO。
- 在非公平锁的实现中,线程在尝试获取锁时仍然会先去查看队列头节点的状态,如果头节点是释放锁的状态,则当前线程直接获取锁,而不需要进入队列等待。只有当头节点是占用锁的状态时,当前线程才会进入队列排队等待锁的释放。因此,队列中的节点仍然会被考虑,只是在唤醒线程时可能会考虑头节点外的其他节点。 因此,非公平锁可以在一定程度上提高系统的吞吐量,但它仍然会考虑队列中的节点,而不是完全不考虑。
为什么需要可重入锁
- 不可重入锁,极其容易发生死锁。
// 定义一个不可重入锁类 class NonReentrantLock { private boolean isLocked = false; public synchronized void lock() throws InterruptedException { while (isLocked) { wait(); } isLocked = true; } public synchronized void unlock() { isLocked = false; notify(); } } // 定义一个测试类 class Test { NonReentrantLock lock = new NonReentrantLock(); public void methodA() throws InterruptedException { lock.lock(); // 获取锁 System.out.println("执行方法A"); methodB(); // 调用方法B lock.unlock(); // 释放锁 } public void methodB() throws InterruptedException { lock.lock(); // 尝试获取锁,但会被阻塞,因为锁已经被当前线程持有 System.out.println("执行方法B"); lock.unlock(); // 释放锁 } } // 定义一个主类 public class Main { public static void main(String\[] args) throws InterruptedException { Test test = new Test(); test.methodA(); // 调用方法A,会发生死锁 } }- 因为methodB()无法获取到锁,它就没法执行。导致methodA()迟迟无法结束,遂死锁。