读写锁:高并发场景的“读写分离“利器
在电商大促期间商品详情页的访问量是平时的100倍但商品信息每小时只更新一次。如何让成千上万的用户同时浏览商品又能在管理员更新价格时保证数据一致性答案就是读写锁。一、读写锁为什么它能提升10倍性能想象一下图书馆的规则普通读者可以同时进入一起看书图书管理员整理书架时需要清场其他人不能进入这就是读写锁的核心思想读可以共享写必须独占。传统锁 vs 读写锁性能对比// ❌ 传统synchronized读操作也被阻塞 public class TraditionalCache { private MapString, Object cache new HashMap(); public synchronized Object get(String key) { // 读操作也被串行化 return cache.get(key); } public synchronized void put(String key, Object value) { cache.put(key, value); } } // 问题1000个读请求也要排队性能极差 // ✅ 读写锁读操作可以并发 public class ReadWriteCache { private MapString, Object cache new HashMap(); private ReadWriteLock lock new ReentrantReadWriteLock(); // 读写分离 public Object get(String key) { lock.readLock().lock(); // 获取读锁 try { return cache.get(key); } finally { lock.readLock().unlock(); // 释放读锁 } } public void put(String key, Object value) { lock.writeLock().lock(); // 获取写锁 try { cache.put(key, value); } finally { lock.writeLock().unlock(); // 释放写锁 } } } // 优势1000个读请求可以同时进行互不阻塞二、深入原理读写锁的双重人格1. 核心状态追踪public class ReadWriteLockInternal { // 状态设计高16位表示读锁数量低16位表示写锁数量 private static final int SHARED_SHIFT 16; private static final int SHARED_UNIT (1 SHARED_SHIFT); private static final int MAX_COUNT (1 SHARED_SHIFT) - 1; private static final int EXCLUSIVE_MASK (1 SHARED_SHIFT) - 1; // 获取读锁数量 static int sharedCount(int c) { return c SHARED_SHIFT; } // 获取写锁数量 static int exclusiveCount(int c) { return c EXCLUSIVE_MASK; } // 状态示例 public void demoState() { int state 0; // 获取3个读锁 state 3 * SHARED_UNIT; // 高16位: 3, 低16位: 0 System.out.println(3个读锁: Integer.toBinaryString(state)); // 获取1个写锁 state 1; // 高16位: 3, 低16位: 1 System.out.println(3读1写: Integer.toBinaryString(state)); } }2. 加锁规则详解public class LockRules { /** * 读写锁的黄金规则 * * 1. 读锁获取条件 * - 没有线程持有写锁 * - 没有线程正在等待获取写锁避免写锁饥饿 * * 2. 写锁获取条件 * - 没有线程持有读锁 * - 没有线程持有写锁 * * 3. 锁降级写锁可以降级为读锁 * 4. 锁升级读锁不能升级为写锁避免死锁 */ // 验证规则1读锁可以共享 public void readLockShare() { ReadWriteLock lock new ReentrantReadWriteLock(); // 线程1获取读锁 new Thread(() - { lock.readLock().lock(); System.out.println(线程1获取读锁); try { Thread.sleep(2000); } catch (InterruptedException e) {} lock.readLock().unlock(); }).start(); // 线程2也可以获取读锁 new Thread(() - { try { Thread.sleep(100); } catch (InterruptedException e) {} lock.readLock().lock(); // ✅ 可以获取 System.out.println(线程2也获取了读锁); lock.readLock().unlock(); }).start(); } // 验证规则2写锁是独占的 public void writeLockExclusive() { ReadWriteLock lock new ReentrantReadWriteLock(); // 线程1获取写锁 new Thread(() - { lock.writeLock().lock(); System.out.println(线程1获取写锁); try { Thread.sleep(2000); } catch (InterruptedException e) {} lock.writeLock().unlock(); }).start(); // 线程2尝试获取写锁会被阻塞 new Thread(() - { try { Thread.sleep(100); } catch (InterruptedException e) {} System.out.println(线程2尝试获取写锁...); lock.writeLock().lock(); // ❌ 这里会阻塞 System.out.println(线程2获取写锁); lock.writeLock().unlock(); }).start(); } // 验证规则3读写互斥 public void readWriteMutualExclusion() { ReadWriteLock lock new ReentrantReadWriteLock(); // 线程1获取写锁 new Thread(() - { lock.writeLock().lock(); System.out.println(写锁已获取开始修改数据); try { Thread.sleep(1000); } catch (InterruptedException e) {} System.out.println(修改完成); lock.writeLock().unlock(); }).start(); // 线程2尝试获取读锁会被阻塞 new Thread(() - { try { Thread.sleep(100); } catch (InterruptedException e) {} System.out.println(尝试获取读锁...); lock.readLock().lock(); // ❌ 等待写锁释放 System.out.println(终于获取到读锁); lock.readLock().unlock(); }).start(); } }三、高级特性锁降级与公平性1. 锁降级写锁变读锁public class LockDowngrade { /** * 锁降级持有写锁的线程可以获取读锁然后释放写锁 * 目的在修改数据后继续保持数据可见性同时允许其他读操作 */ public void processData() { ReadWriteLock lock new ReentrantReadWriteLock(); ListInteger data new ArrayList(); // 1. 获取写锁 lock.writeLock().lock(); try { // 修改数据 data.add(1); data.add(2); // 2. 关键步骤获取读锁锁降级开始 lock.readLock().lock(); } finally { // 3. 释放写锁但还持有读锁 lock.writeLock().unlock(); } // 4. 此时只有读锁没有写锁 // 其他线程可以获取读锁但不能获取写锁 try { // 读取数据 System.out.println(数据: data); // 可以长时间持有读锁因为读锁是共享的 // 其他读操作不会被阻塞 } finally { // 5. 释放读锁 lock.readLock().unlock(); } } // ❌ 错误锁升级会导致死锁 public void dangerousLockUpgrade() { ReadWriteLock lock new ReentrantReadWriteLock(); lock.readLock().lock(); try { // 尝试获取写锁 - 这会死锁 // 因为当前线程持有读锁需要等待所有读锁释放 // 但当前线程自己就持有一个读锁永远等不到 lock.writeLock().lock(); // 死锁位置 } finally { lock.readLock().unlock(); } } }2. 公平 vs 非公平public class FairnessExample { /** * 公平锁按照请求顺序分配锁 * 非公平锁允许插队吞吐量更高 */ public void compareFairness() { // 非公平锁默认性能高但可能导致饥饿 ReadWriteLock nonfairLock new ReentrantReadWriteLock(false); // 公平锁按顺序分配避免饥饿 ReadWriteLock fairLock new ReentrantReadWriteLock(true); // 测试大量读操作中间插入写操作 testLockPerformance(nonfairLock, 非公平锁); testLockPerformance(fairLock, 公平锁); } private void testLockPerformance(ReadWriteLock lock, String lockType) { long start System.currentTimeMillis(); // 创建10个读线程 for (int i 0; i 10; i) { new Thread(() - { lock.readLock().lock(); try { Thread.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { lock.readLock().unlock(); } }).start(); } // 中间插入1个写线程 new Thread(() - { lock.writeLock().lock(); try { Thread.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { lock.writeLock().unlock(); } }).start(); long end System.currentTimeMillis(); System.out.println(lockType 耗时: (end - start) ms); } /** * 输出结果通常 * 非公平锁耗时: 约200ms * 公平锁耗时: 约1100ms * * 原因公平锁严格按照顺序执行写操作必须等前面所有读操作完成 */ }四、实战场景五大经典应用场景1缓存系统最常用Component Slf4j public class CacheWithReadWriteLock { // 两级缓存本地缓存 Redis private final MapString, CacheItem localCache new ConcurrentHashMap(); private final ReadWriteLock lock new ReentrantReadWriteLock(); private final RedisTemplateString, Object redisTemplate; Data AllArgsConstructor static class CacheItem { private Object value; private long expireTime; public boolean isExpired() { return System.currentTimeMillis() expireTime; } } /** * 获取缓存读多写少场景 */ public Object get(String key) { // 1. 尝试从本地缓存读取加读锁 lock.readLock().lock(); try { CacheItem item localCache.get(key); if (item ! null !item.isExpired()) { log.debug(缓存命中本地: {}, key); return item.getValue(); } } finally { lock.readLock().unlock(); } // 2. 本地没有尝试从Redis获取 Object value redisTemplate.opsForValue().get(key); if (value ! null) { // 3. 更新到本地缓存需要写锁 lock.writeLock().lock(); try { localCache.put(key, new CacheItem(value, System.currentTimeMillis() 30000)); // 30秒过期 } finally { lock.writeLock().unlock(); } log.debug(缓存命中Redis: {}, key); } else { log.debug(缓存未命中: {}, key); } return value; } /** * 更新缓存写操作较少 */ public void put(String key, Object value, long ttlSeconds) { // 同时更新本地缓存和Redis lock.writeLock().lock(); try { // 1. 更新本地缓存 localCache.put(key, new CacheItem(value, System.currentTimeMillis() ttlSeconds * 1000)); // 2. 异步更新Redis不阻塞读操作 CompletableFuture.runAsync(() - { redisTemplate.opsForValue().set(key, value, ttlSeconds, TimeUnit.SECONDS); }); log.info(缓存更新: {}, key); } finally { lock.writeLock().unlock(); } } /** * 批量获取读锁提升性能 */ public MapString, Object batchGet(ListString keys) { MapString, Object result new HashMap(); lock.readLock().lock(); try { for (String key : keys) { CacheItem item localCache.get(key); if (item ! null !item.isExpired()) { result.put(key, item.getValue()); } } } finally { lock.readLock().unlock(); } return result; } }场景2配置管理中心Service public class ConfigCenter { // 配置信息读多写少 private final MapString, String configs new HashMap(); private final ReadWriteLock lock new ReentrantReadWriteLock(); private volatile long lastModified 0; // 配置监听器 private final ListConfigListener listeners new CopyOnWriteArrayList(); /** * 获取配置高频读操作 */ public String getConfig(String key) { lock.readLock().lock(); try { return configs.get(key); } finally { lock.readLock().unlock(); } } /** * 获取所有配置需要一致性视图 */ public MapString, String getAllConfigs() { lock.readLock().lock(); try { // 返回防御性副本 return new HashMap(configs); } finally { lock.readLock().unlock(); } } /** * 更新配置低频写操作 */ public void updateConfigs(MapString, String newConfigs) { lock.writeLock().lock(); try { // 1. 更新配置 configs.clear(); configs.putAll(newConfigs); lastModified System.currentTimeMillis(); // 2. 通知监听器 notifyListeners(newConfigs); log.info(配置已更新共{}项, newConfigs.size()); } finally { lock.writeLock().unlock(); } } /** * 热更新单个配置使用锁降级 */ public void hotUpdate(String key, String value) { // 1. 获取写锁 lock.writeLock().lock(); try { // 2. 更新配置 configs.put(key, value); // 3. 锁降级获取读锁 lock.readLock().lock(); } finally { // 4. 释放写锁 lock.writeLock().unlock(); } // 5. 此时持有读锁可以安全地通知监听器 try { notifyListeners(key, value); } finally { // 6. 释放读锁 lock.readLock().unlock(); } } }场景3股票行情系统public class StockQuotationSystem { // 股票行情数据 private final MapString, StockQuote quotes new ConcurrentHashMap(); private final ReadWriteLock lock new ReentrantReadWriteLock(); Data static class StockQuote { private String symbol; private BigDecimal price; private BigDecimal change; private long timestamp; private long volume; } /** * 获取股票报价每秒数千次查询 */ public StockQuote getQuote(String symbol) { lock.readLock().lock(); try { StockQuote quote quotes.get(symbol); if (quote null) { return null; } // 检查数据是否过时超过5秒 if (System.currentTimeMillis() - quote.getTimestamp() 5000) { // 触发异步更新 CompletableFuture.runAsync(() - refreshQuote(symbol)); } return quote; } finally { lock.readLock().unlock(); } } /** * 批量更新行情每3秒一次 */ public void batchUpdateQuotes(ListStockQuote newQuotes) { lock.writeLock().lock(); try { for (StockQuote quote : newQuotes) { quote.setTimestamp(System.currentTimeMillis()); quotes.put(quote.getSymbol(), quote); } log.debug(批量更新{}条行情, newQuotes.size()); } finally { lock.writeLock().unlock(); } } /** * 计算市场指数需要读取所有股票 */ public MarketIndex calculateIndex() { MarketIndex index new MarketIndex(); lock.readLock().lock(); try { BigDecimal totalMarketCap BigDecimal.ZERO; int stockCount 0; for (StockQuote quote : quotes.values()) { // 计算总市值等 totalMarketCap totalMarketCap.add(quote.getPrice()); stockCount; } index.setAveragePrice(totalMarketCap.divide( BigDecimal.valueOf(stockCount), 2, RoundingMode.HALF_UP)); index.setStockCount(stockCount); index.setTimestamp(System.currentTimeMillis()); } finally { lock.readLock().unlock(); } return index; } }场景4文档协同编辑public class CollaborativeDocument { private StringBuilder content new StringBuilder(); private final ReadWriteLock lock new ReentrantReadWriteLock(); private final ListDocumentListener listeners new CopyOnWriteArrayList(); // 版本控制 private volatile int version 0; /** * 读取文档多个用户可以同时查看 */ public String getContent() { lock.readLock().lock(); try { return content.toString(); } finally { lock.readLock().unlock(); } } /** * 获取指定范围内容 */ public String getContentRange(int start, int end) { lock.readLock().lock(); try { return content.substring(start, Math.min(end, content.length())); } finally { lock.readLock().unlock(); } } /** * 编辑文档需要排他锁 */ public void edit(int position, String newText, String author) { lock.writeLock().lock(); try { // 保存历史版本 saveVersion(); // 执行编辑 if (position content.length()) { content.insert(position, newText); version; // 通知其他用户 notifyEdit(position, newText, author, version); log.info(用户{}在位置{}插入{}个字符, author, position, newText.length()); } } finally { lock.writeLock().unlock(); } } /** * 复杂操作查找并替换 */ public int findAndReplace(String find, String replace) { lock.writeLock().lock(); try { int count 0; int index 0; while ((index content.indexOf(find, index)) ! -1) { content.replace(index, index find.length(), replace); count; index replace.length(); } if (count 0) { version; log.info(完成{}处替换, count); } return count; } finally { lock.writeLock().unlock(); } } }场景5数据库连接池public class DatabaseConnectionPool { // 连接池 private final ListConnection idleConnections new ArrayList(); private final ListConnection activeConnections new ArrayList(); private final ReadWriteLock lock new ReentrantReadWriteLock(true); // 公平锁 private final int maxPoolSize; public DatabaseConnectionPool(int maxPoolSize) { this.maxPoolSize maxPoolSize; } /** * 获取连接高频操作 */ public Connection getConnection() throws SQLException, InterruptedException { long startTime System.currentTimeMillis(); long timeout 5000; // 5秒超时 while (System.currentTimeMillis() - startTime timeout) { lock.writeLock().lock(); try { // 1. 检查是否有空闲连接 if (!idleConnections.isEmpty()) { Connection conn idleConnections.remove(0); activeConnections.add(conn); return conn; } // 2. 如果没有空闲连接但可以创建新连接 if (activeConnections.size() idleConnections.size() maxPoolSize) { Connection conn createNewConnection(); activeConnections.add(conn); return conn; } } finally { lock.writeLock().unlock(); } // 3. 等待其他连接释放 Thread.sleep(100); } throw new SQLException(获取数据库连接超时); } /** * 释放连接 */ public void releaseConnection(Connection conn) { lock.writeLock().lock(); try { if (activeConnections.remove(conn)) { idleConnections.add(conn); log.debug(连接已释放空闲连接数: {}, idleConnections.size()); } } finally { lock.writeLock().unlock(); } } /** * 监控连接池状态只读操作 */ public PoolStats getPoolStats() { PoolStats stats new PoolStats(); lock.readLock().lock(); try { stats.setActiveCount(activeConnections.size()); stats.setIdleCount(idleConnections.size()); stats.setTotalCount(activeConnections.size() idleConnections.size()); stats.setMaxPoolSize(maxPoolSize); } finally { lock.readLock().unlock(); } return stats; } /** * 清理空闲连接 */ public void cleanupIdleConnections() { lock.writeLock().lock(); try { IteratorConnection it idleConnections.iterator(); while (it.hasNext()) { Connection conn it.next(); if (isConnectionIdleTooLong(conn)) { closeConnection(conn); it.remove(); } } } finally { lock.writeLock().unlock(); } } }五、性能优化读写锁的最佳实践1. 选择合适的锁实现public class LockSelectionGuide { /** * 如何选择合适的锁 * * 场景 推荐锁类型 理由 * 读多写少99%读 ReentrantReadWriteLock 读锁并发 * 读写均衡 StampedLock 乐观读提升性能 * 写多读少 ReentrantLock 避免读写锁开销 * 短时持有 synchronized JVM优化好 */ // 对比不同锁的性能 public void benchmarkLocks() { int readThreads 100; int writeThreads 5; // 测试1ReentrantReadWriteLock testReadWriteLock(readThreads, writeThreads); // 测试2StampedLockJava 8 testStampedLock(readThreads, writeThreads); // 测试3synchronized testSynchronized(readThreads, writeThreads); } // StampedLock示例乐观读 public void stampedLockExample() { StampedLock stampedLock new StampedLock(); ListString data new ArrayList(); // 乐观读 long stamp stampedLock.tryOptimisticRead(); // 读取数据 String value data.toString(); // 检查在读期间是否有写操作 if (!stampedLock.validate(stamp)) { // 有写操作升级为悲观读 stamp stampedLock.readLock(); try { value data.toString(); } finally { stampedLock.unlockRead(stamp); } } } }2. 避免常见陷阱public class CommonPitfalls { /** * 陷阱1读锁中调用写锁死锁 */ public void deadlockTrap() { ReadWriteLock lock new ReentrantReadWriteLock(); lock.readLock().lock(); try { // 在持有读锁时尝试获取写锁 lock.writeLock().lock(); // ❌ 死锁 } finally { lock.readLock().unlock(); } } /** * 陷阱2忘记释放锁 */ public void forgetUnlock() { ReadWriteLock lock new ReentrantReadWriteLock(); // 正确写法使用try-finally lock.readLock().lock(); try { // 业务逻辑 } finally { lock.readLock().unlock(); // ✅ 确保释放 } // 错误写法可能忘记解锁 lock.readLock().lock(); // 业务逻辑 // 如果这里抛异常锁永远不会释放 lock.readLock().unlock(); } /** * 陷阱3锁粒度过大 */ public void coarseGrainedLock() { // ❌ 错误整个方法加写锁 public synchronized void updateAll() { // 更新A // 更新B // 更新C } // ✅ 正确细粒度锁 private final ReadWriteLock lockA new ReentrantReadWriteLock(); private final ReadWriteLock lockB new ReentrantReadWriteLock(); private final ReadWriteLock lockC new ReentrantReadWriteLock(); public void updateAllSeparately() { // 可以并行更新A、B、C CompletableFuture.allOf( CompletableFuture.runAsync(this::updateA), CompletableFuture.runAsync(this::updateB), CompletableFuture.runAsync(this::updateC) ).join(); } } }六、监控与调优1. 监控读写锁状态Slf4j public class ReadWriteLockMonitor { private final ReadWriteLock lock new ReentrantReadWriteLock(true); private final AtomicLong readCount new AtomicLong(0); private final AtomicLong writeCount new AtomicLong(0); private final AtomicLong readWaitTime new AtomicLong(0); private final AtomicLong writeWaitTime new AtomicLong(0); public T T executeWithReadLock(SupplierT supplier) { long startWait System.nanoTime(); lock.readLock().lock(); long endWait System.nanoTime(); readWaitTime.addAndGet(endWait - startWait); readCount.incrementAndGet(); try { return supplier.get(); } finally { lock.readLock().unlock(); } } public void executeWithWriteLock(Runnable task) { long startWait System.nanoTime(); lock.writeLock().lock(); long endWait System.nanoTime(); writeWaitTime.addAndGet(endWait - startWait); writeCount.incrementAndGet(); try { task.run(); } finally { lock.writeLock().unlock(); } } public void printStats() { long totalReads readCount.get(); long totalWrites writeCount.get(); long totalOps totalReads totalWrites; if (totalOps 0) { double readRatio (double) totalReads / totalOps * 100; double avgReadWait (double) readWaitTime.get() / totalReads / 1_000_000; // 转ms double avgWriteWait totalWrites 0 ? (double) writeWaitTime.get() / totalWrites / 1_000_000 : 0; log.info(读写锁统计:); log.info( 读操作占比: {:.2f}%, readRatio); log.info( 平均读等待: {:.3f}ms, avgReadWait); log.info( 平均写等待: {:.3f}ms, avgWriteWait); log.info( 读写比例: {}:1, totalWrites 0 ? totalReads / totalWrites : ∞); // 根据统计结果给出建议 if (readRatio 80) { log.info(✅ 读多写少读写锁适用性良好); } else if (readRatio 20) { log.info(⚠️ 写多读少考虑使用普通锁); } } } }七、选型决策矩阵场景特征推荐方案理由读:写 10:1ReentrantReadWriteLock读锁并发优势明显读多写少但读很快StampedLock乐观读避免读锁开销需要锁降级ReentrantReadWriteLock支持锁降级需要锁升级无读写锁不支持需重新设计竞争激烈公平锁避免饥饿追求最大吞吐非公平锁允许插队简单场景synchronized代码简洁需要超时ReentrantReadWriteLock支持tryLock八、总结读写锁是读多写少场景下的性能利器但它不是银弹。正确使用读写锁需要理解原理读共享写独占分析场景评估读写比例合理选型根据场景选择锁类型注意陷阱避免死锁、忘记解锁持续监控根据性能数据调优记住任何锁都有开销无锁设计才是最高境界。在满足需求的前提下尽量减少锁的使用。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2631248.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!