Java并发编程:ReentrantReadWriteLock读写锁
前言在Java并发编程中锁机制是保证线程安全的重要手段。synchronized和ReentrantLock都是排他锁同一时刻只允许一个线程访问共享资源。但在实际业务场景中读操作往往远多于写操作如果多个读线程之间也要互相等待会严重影响系统性能。ReentrantReadWriteLock读写锁正是为了解决这个问题而设计的。它维护了一对锁读锁和写锁通过读写分离的策略大幅提升并发性能。一、读写锁的核心特性锁类型特性说明读锁共享锁多个线程可同时持有读锁写锁排他锁同一时刻只能有一个线程持有写锁读写锁遵循以下基本原则读-读共享多个读线程可以同时执行写-写互斥多个写线程不能同时执行读-写互斥读操作和写操作不能同时进行二、三种场景代码实战2.1 读读共享读锁是共享锁多个线程可以同时获取读锁互不阻塞。public class ReadReadTest { public static void main(String[] args) { MyTask myTask new MyTask(); new Thread(() - myTask.read(), t1).start(); new Thread(() - myTask.read(), t2).start(); } static class MyTask { private final ReentrantReadWriteLock lock new ReentrantReadWriteLock(); public void read() { try { lock.readLock().lock(); System.out.println(Thread.currentThread().getName() start); Thread.sleep(10000); System.out.println(Thread.currentThread().getName() end); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.readLock().unlock(); } } } }运行结果t1 start t2 start t1 end t2 end可以看到t1和t2几乎同时开始读锁不互斥。2.2 写写互斥写锁是排他锁同一时刻只能有一个线程持有写锁。public class WriteWriteTest { public static void main(String[] args) { MyTask myTask new MyTask(); new Thread(() - myTask.write(), t1).start(); new Thread(() - myTask.write(), t2).start(); } static class MyTask { private final ReentrantReadWriteLock lock new ReentrantReadWriteLock(); public void write() { try { lock.writeLock().lock(); System.out.println(Thread.currentThread().getName() start); Thread.sleep(10000); System.out.println(Thread.currentThread().getName() end); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.writeLock().unlock(); } } } }运行结果t1 start t1 end t2 start t2 endt2必须等待t1释放写锁后才能执行写锁互斥。2.3 读写互斥读写操作互斥读操作进行时写操作必须等待反之亦然。public class ReadWriteTest { public static void main(String[] args) { MyTask myTask new MyTask(); Thread t1 new Thread(() - myTask.read(), t1); Thread t2 new Thread(() - myTask.write(), t2); t1.start(); // 确保t1先获取读锁 Thread.sleep(2000); t2.start(); } static class MyTask { private final ReentrantReadWriteLock lock new ReentrantReadWriteLock(); public void read() { try { lock.readLock().lock(); System.out.println(Thread.currentThread().getName() start); Thread.sleep(10000); System.out.println(Thread.currentThread().getName() end); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.readLock().unlock(); } } public void write() { try { lock.writeLock().lock(); System.out.println(Thread.currentThread().getName() start); Thread.sleep(10000); System.out.println(Thread.currentThread().getName() end); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.writeLock().unlock(); } } } }运行结果t1 start t1 end t2 start t2 endt2必须等待t1读完才能开始写读写互斥。三、读写锁的适用场景读写锁特别适合读多写少的业务场景场景是否适用原因缓存系统✅ 非常适合大量读操作偶尔更新配置中心✅ 非常适合配置读取频繁变更极少计数器❌ 不适合读写比例接近1:1账户转账❌ 不适合写操作频繁锁竞争严重四、注意事项4.1 锁降级ReentrantReadWriteLock支持锁降级写锁可以降级为读锁但读锁不能升级为写锁。// 锁降级允许 writeLock.lock(); readLock.lock(); // 写锁持有状态下获取读锁 writeLock.unlock(); // 仍然持有读锁 // 锁升级不允许会死锁 readLock.lock(); writeLock.lock(); // 线程永久阻塞5.2 公平性选择与ReentrantLock一样读写锁也支持公平/非公平模式非公平模式默认吞吐量更高但可能造成写线程饥饿公平模式线程按请求顺序获取锁避免饥饿// 公平读写锁 ReentrantReadWriteLock fairLock new ReentrantReadWriteLock(true);5.3 重入特性读写锁支持锁重入但需要注意读线程可以重复获取读锁写线程可以重复获取写锁也可以获取读锁锁降级六、总结对比项ReentrantLockReentrantReadWriteLock锁类型排他锁读写分离读并发不并发并发写并发不并发不并发适用场景读写均衡读多写少最佳实践在读操作远多于写操作的场景下优先考虑使用ReentrantReadWriteLock它可以显著提升系统吞吐量让并发性能得到质的飞跃。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2641692.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!