java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > ReadWriteLock读写锁

关于ReadWriteLock读写锁的使用及说明

作者:找不到、了

这篇文章主要介绍了关于ReadWriteLock读写锁的使用及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

ReentrantReadWriteLock实现了ReadWriteLock接口。位于java.util.concurrent.locks;

1、普通锁

读写互斥,如ReentrantLock。

1.1、原理

代码示例:

ReentrantLock lock = new ReentrantLock();

void read() {
    lock.lock();
    try {
        // 读取数据
    } finally {
        lock.unlock();
    }
}

void write() {
    lock.lock();
    try {
        // 写入数据
    } finally {
        lock.unlock();
    }
}

1.2、特点

2、ReadWriteLock

读写分离机制。

2.1、核心思想

规则读锁与读锁不互斥读锁与写锁互斥写锁与写锁互斥

读锁(共享锁)

写锁(排他锁)

锁升级/降级规则

代码示例:

ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
Lock readLock = readWriteLock.readLock();
Lock writeLock = readWriteLock.writeLock();

void read() {
    readLock.lock();
    try {
        // 读取数据(多个线程可同时读)
    } finally {
        readLock.unlock();
    }
}

void write() {
    writeLock.lock();
    try {
        // 写入数据(独占)
    } finally {
        writeLock.unlock();
    }
}

为什么读锁和写锁可以“部分共存”?

2.2、特点

1、高效

适合高并发读的场景。

2、缓存读取和更新

class Cache {
    private Object data;
    private ReadWriteLock lock = new ReentrantReadWriteLock();

    void get() {
        lock.readLock().lock();
        try {
            // 多个线程可同时读取
            return data;
        } finally {
            lock.readLock().unlock();
        }
    }

    void put(Object newData) {
        lock.writeLock().lock();
        try {
            // 写入时独占
            data = newData;
        } finally {
            lock.writeLock().unlock();
        }
    }
}

2.3、锁共存

写锁不能与读锁或写锁共存。具体是为什么,可参考以下数据一致性和state字段来进行分析。

1. 数据一致性要求

写操作必须独占:如果允许写锁与读锁或写锁共存,可能导致:

2. 内部实现限制

读写锁的实现

2.4、关键字段

以下是常用的方法:

2.5、获取流程

1、写锁

2、读锁

小结

如何选择哪种锁,可根据以下场景进行分析:

选择普通锁

选择读写锁

对比

普通锁 vsReadWriteLock:

3、写锁饥饿

3.1、原因

1. 优先级

2. 等待队列机制

AQS(AbstractQueuedSynchronizer)维护一个 FIFO 队列

非公平模式下

示例:

ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

// 线程 A: 读线程
lock.readLock().lock();
try {
    while (true) {
        // 持续读取(不释放读锁)
    }
} finally {
    lock.readLock().unlock();
}

// 线程 B: 写线程
lock.writeLock().lock(); // 被阻塞,永远无法获取写锁

3.2、实现原理

1. 写锁获取流程

检查当前是否有写锁(通过exclusiveCount判断)。

检查是否有读锁(通过sharedCount判断)。

非公平模式下

公平模式下

2. 写锁释放流程

  1. 释放写锁后,唤醒等待队列中的线程。

非公平模式下

3.3、避免写锁饥饿

1. 使用公平模式(Fair Mode)

效果

代码示例:

ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true); // 公平模式

void read() {
    lock.readLock().lock();
    try {
        // 读取数据
    } finally {
        lock.readLock().unlock();
    }
}

void write() {
    lock.writeLock().lock();
    try {
        // 写入数据
    } finally {
        lock.writeLock().unlock();
    }
}

公平模式下和非公平模式下:

2.限制读锁的持有时间

避免读线程长期占用读锁

3. 使用StampedLock

在Java 8+,StampedLock提供更灵活的读写锁策略

代码示例:

StampedLock lock = new StampedLock();

void read() {
    long stamp = lock.tryOptimisticRead();
    if (lock.validate(stamp)) {
        // 乐观读取(不阻塞写锁)
    }
}

void write() {
    long stamp = lock.writeLock();
    try {
        // 写入数据
    } finally {
        lock.unlockWrite(stamp);
    }
}

总结

通过合理选择锁策略,可以在高并发场景下平衡性能与公平性!

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:
阅读全文