本文共 3183 字,大约阅读时间需要 10 分钟。
读写锁(ReentrantReadWriteLock)是一种并发控制机制,专为处理读多写少的场景设计。它允许多个线程同时持有读锁,从而提高系统性能。读写锁的核心思想是在读操作之间支持并发,而在写操作与读操作、写操作之间必须阻塞,直到冲突资源解除。
读写锁的核心优势在于其效率和灵活性。通过持有一个共享锁,读操作可以在不影响其他读操作的情况下进行,而写操作则需要独占资源,确保数据一致性。在实际应用中,读写锁常用于缓存管理、数据库连接池等场景。
读写锁通常通过数据容器类(DataContainer)来实现。在这种模式中,数据容器类提供了读锁和写锁两种操作:
package cn.knightzz.juc.reentrantlock.readwrite;import lombok.extern.Slf4j;import java.util.concurrent.TimeUnit;import java.util.concurrent.locks.ReentrantReadWriteLock;@Slf4j@SuppressWarnings("all")public class DataContainer { private Object data; private ReentrantReadWriteLock rw = new ReentrantReadWriteLock(); private ReentrantReadWriteLock.ReadLock readLock = rw.readLock(); private ReentrantReadWriteLock.WriteLock writeLock = rw.writeLock(); public Object read() { log.debug("获取读取锁 ... "); readLock.lock(); try { log.debug("开始读取 ... "); TimeUnit.SECONDS.sleep(1); return data; } catch (InterruptedException e) { throw new RuntimeException(e); } finally { log.debug("释放读锁 ... "); readLock.unlock(); } } public void write() { log.debug("获取写入锁.."); writeLock.lock(); try { log.debug("开始写入数据 ... "); TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { throw new RuntimeException(e); } finally { log.debug("释放写入锁 ... "); writeLock.unlock(); } }} 在多线程环境中,读锁可以同时被多个线程获取。例如,在以下测试中:
public static void main(String[] args) throws IOException, InterruptedException { DataContainer container = new DataContainer(); for (int i = 1; i < 5; i++) { new Thread(() -> container.read(), "t" + i).start(); } System.in.read();} 可以看到多个线程同时获取读锁,并在不影响彼此的情况下执行读操作。这种特性使得读锁非常适合读密度高的场景。
在写锁场景中,写操作是独占的。以下测试展示了写锁的行为:
public static void main(String[] args) throws IOException, InterruptedException { DataContainer container = new DataContainer(); for (int i = 1; i < 5; i++) { new Thread(() -> container.write(), "t" + i).start(); } System.in.read();} 在这个测试中,写操作会相互阻塞,直到先前的写操作完成。可以看到,虽然多个线程尝试获取写锁,但只有一个线程能够真正执行写操作。
读写锁的独特之处在于,它不仅支持读写锁的相互阻塞,还允许在读锁持有的情况下,写锁无法获取。以下测试展示了这种行为:
public static void main(String[] args) throws IOException, InterruptedException { DataContainer container = new DataContainer(); new Thread(() -> container.read(), "t1").start(); Thread.sleep(TimeUnit.SECONDS.toMillis(1)); new Thread(() -> container.write(), "t2").start();} 在这个测试中,t1线程首先获取读锁并执行读操作。t2线程在读锁释放后,才能获取写锁并执行写操作。这充分体现了读写锁的相互阻塞特性。
读写锁不支持条件变量,这意味着无法在持有锁的情况下进行条件检查。这种限制使得读写锁的实现更加简单,但也带来了使用上的局限性。例如,如果需要在持有读锁的情况下进行条件检查,读写锁无法满足需求。
此外,重入锁的行为也需要注意:在持有读锁的情况下,无法获取写锁。这意味着如果一个线程在持有读锁的情况下尝试获取写锁,将导致等待直到读锁被释放。
读写锁的主要应用场景在于缓存一致性管理。通过使用读写锁,可以确保在多线程环境中,缓存的读写操作能够高效且安全地进行。在实际应用中,读写锁可以用来保护共享资源,避免数据不一致的问题。
总结来说,读写锁是一种高效的并发控制机制,特别适用于读多于写的场景。通过合理使用读锁和写锁,可以在多线程环境中实现资源共享和数据保护。
转载地址:http://hevfk.baihongyu.com/