ReentrantReadWriteLock包含了两个锁,一个是读操作相关的锁,称为共享锁;一个是写相关的锁,称为独占锁;
- 线程进入读锁的前提条件:
没有其他线程的写锁,
没有写请求或者有写请求,但调用线程和持有锁的线程是同一个。
- 线程进入写锁的前提条件:
没有其他线程的读锁
没有其他线程的写锁
- 而读写锁有以下三个重要的特性:
(1)公平选择性:支持非公平(默认)和公平的锁获取方式,吞吐量还是非公平优于公平。
(2)重入:读锁和写锁都支持线程重进入。
(3)锁降级:遵循获取写锁、获取读锁再释放写锁的次序,写锁能够降级成为读锁。
ReentrantLock是一个排他锁,同一时间只允许一个线程访问,而ReentrantReadWriteLock允许多个读线程同时访问,但不允许写线程和读线程、写线程和写线程同时访问。相对于排他锁,提高了并发性。在实际应用中,大部分情况下对共享数据(如缓存)的访问都是读操作远多于写操作,这时ReentrantReadWriteLock能够提供比排他锁更好的并发性和吞吐量。
读写锁将AQS中的state整型做了一下处理,它是一个int型变量一共4个字节32位,那么可以读写状态就可以各占16位——高16位表示读,低16位表示写。
- 用法:
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();// 读写锁
读读共享
public String readI() {
try {
lock.readLock().lock();// 占用读锁
log.info("threadName -> {} 占用读锁,i->{}", Thread.currentThread().getName(), i);
Thread.sleep(2 * 1000);
} catch (InterruptedException e) {
} finally {
log.info("threadName -> {} 释放读锁,i->{}", Thread.currentThread().getName(), i);
lock.readLock().unlock();// 释放读锁
}
return i + "";
}
写写互斥
public void addI() {
try {
lock.writeLock().lock();// 占用写锁
log.info("threadName -> {} 占用写锁,i->{}", Thread.currentThread().getName(), i);
Thread.sleep(2 * 1000);
i++;
} catch (InterruptedException e) {
} finally {
log.info("threadName -> {} 释放写锁,i->{}", Thread.currentThread().getName(), i);
lock.writeLock().unlock();// 释放写锁
}
}
读写互斥
public String readI() {
try {
lock.readLock().lock();// 占用读锁
log.info("threadName -> {} 占用读锁,i->{}", Thread.currentThread().getName(), i);
Thread.sleep(2 * 1000);
} catch (InterruptedException e) {
} finally {
log.info("threadName -> {} 释放读锁,i->{}", Thread.currentThread().getName(), i);
lock.readLock().unlock();// 释放读锁
}
return i + "";
}
public void addI() {
try {
lock.writeLock().lock();// 占用写锁
log.info("threadName -> {} 占用写锁,i->{}", Thread.currentThread().getName(), i);
Thread.sleep(2 * 1000);
i++;
} catch (InterruptedException e) {
} finally {
log.info("threadName -> {} 释放写锁,i->{}", Thread.currentThread().getName(), i);
lock.writeLock().unlock();// 释放写锁
}
}