读写锁
读读共享, 读写和写写都互斥.因为锁粒度更小, 性能比ReentrantLock好.
读写锁应用场景
- 读多写少
- 缓存
ReentrantReadWriteLock
ReentrantReadWriteLock是基于aqs实现的一把读写锁
使用示例:
@Slf4j
public class ReadWriteDemo {
private static int count;
private static ReentrantReadWriteLock.ReadLock readLock;
private static ReentrantReadWriteLock.WriteLock writeLock;
static {
ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
readLock = readWriteLock.readLock();
writeLock = readWriteLock.writeLock();
}
public static int read() {
readLock.lock();
log.info("获取读锁");
try {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return count;
} finally {
readLock.unlock();
log.info("释放读锁");
}
}
public static void write() {
writeLock.lock();
log.info("获取写锁");
try {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
count++;
} finally {
writeLock.unlock();
log.info("释放写锁");
}
}
public static void main(String[] args) {
new Thread(ReadWriteDemo::read).start();
new Thread(ReadWriteDemo::read).start();
new Thread(ReadWriteDemo::write).start();
new Thread(ReadWriteDemo::write).start();
}
}
注意事项:
- 读锁是必要的, 为了保证数据可见性
- 重入锁可降级, 不可升级,. 也就是已经获取写锁的线程, 可以重入获取读锁, 反之不行
StampedLock
乐观读, 比ReentrantReadWriteLock读写锁性能更高, 没有写操作时不加锁.
不支持可重入, 不支持公平锁, 适合读多写少.
示例:
@Slf4j
public class StampedLockDemo {
private static StampedLock stampedLock = new StampedLock();
private static int x;
private static int readX() {
// 获取一个乐观读, 无锁操作
long stamp = stampedLock.tryOptimisticRead();
// 业务操作
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
log.error("error", e);
}
// 判断是否有写入
if (!stampedLock.validate(stamp)) {
// 如果有写入, 加读锁
stamp = stampedLock.readLock();
log.info("获取读锁");
try {
return x;
} finally {
stampedLock.unlockRead(stamp);
log.info("释放读锁");
}
} else {
log.info("无改动, 不用加锁");
}
return x;
}
private static void write(int val) {
long stamp = stampedLock.writeLock();
log.info("获取写锁");
try {
x = val;
} finally {
stampedLock.unlockWrite(stamp);
log.info("释放写锁");
}
}
public static void main(String[] args) {
new Thread(() -> readX()).start();
// new Thread(() -> readX()).start();
new Thread(() -> write(19)).start();
}
}