读写锁 ReentrantReadWriteLock
排它锁:
之前的Synchronized和ReentrantLock都是排他锁,默认只有一个线程可以占用
读写锁:
读写锁,同一时刻允许多个读线程同时访问,但是写线程访问的时候,所有的读和写都被阻塞,最适宜与读多写少的情况
通过解释,我们可以知道读写锁,最常用的就是读多写少的场景,读写锁相比于普通的排它锁,提高了很高的读取性能
接口:
ReadWriteLock
通过接口可以看到需要,实现一个读锁,和一个写锁
实现类:
ReemtramtReadWriteLock
但是他的内部的读锁和写锁,还是实现了Lock接口
演示读写锁,在读多写少的情况下,读写锁,相对于Sync排它锁的性能提升
定义商品实体类
package org.dance.day4.rw; /** * 类说明:商品的实体类 */ public class GoodsInfo { private final String name; //总销售额 private double totalMoney; //库存数 private int storeNumber; public GoodsInfo(String name, int totalMoney, int storeNumber) { this.name = name; this.totalMoney = totalMoney; this.storeNumber = storeNumber; } public double getTotalMoney() { return totalMoney; } public int getStoreNumber() { return storeNumber; } public void changeNumber(int sellNumber){ this.totalMoney += sellNumber*25; this.storeNumber -= sellNumber; } }
定义商品服务接口,获取信息有用于读取,设置用于写入
package org.dance.day4.rw; /** * 类说明:商品的服务的接口 */ public interface GoodsService { //获得商品的信息 public GoodsInfo getNum(); //设置商品的数量 public void setNum(int number); }
使用内置锁,写一个实现
package org.dance.day4.rw;
import org.dance.tools.SleepTools; /** * 类说明:用内置锁来实现商品服务接口 */ public class UseSyn implements GoodsService { private GoodsInfo goodsInfo; public UseSyn(GoodsInfo goodsInfo) { this.goodsInfo = goodsInfo; } @Override public synchronized GoodsInfo getNum() { SleepTools.ms(5); return this.goodsInfo; } @Override public synchronized void setNum(int number) { SleepTools.ms(5); goodsInfo.changeNumber(number); } }
工具类
package org.dance.tools; import java.util.concurrent.TimeUnit; /** * 类说明:线程休眠辅助工具类 */ public class SleepTools { /** * 按秒休眠 * @param seconds 秒数 */ public static final void second(int seconds) { try { TimeUnit.SECONDS.sleep(seconds); } catch (InterruptedException e) { } } /** * 按毫秒数休眠 * @param seconds 毫秒数 */ public static final void ms(int seconds) { try { TimeUnit.MILLISECONDS.sleep(seconds); } catch (InterruptedException e) { } } }
调用测试类:
package org.dance.day4.rw; import org.dance.tools.SleepTools; import java.util.Random; /** * 类说明:对商品进行业务的应用 */ public class BusiApp { /** * 读写线程的比例 */ static final int readWriteRatio = 10; /** * 最少线程数 */ static final int minthreadCount = 3; /** * 读操作 */ private static class GetThread implements Runnable{ private GoodsService goodsService; /** * 传入商品 * @param goodsService */ public GetThread(GoodsService goodsService) { this.goodsService = goodsService; } @Override public void run() { long start = System.currentTimeMillis(); // 读取100次数量 for(int i=0;i<100;i++){//操作100次 goodsService.getNum(); } System.out.println(Thread.currentThread().getName()+"读取商品数据耗时:" +(System.currentTimeMillis()-start)+"ms"); } } /** * 写操做 */ private static class SetThread implements Runnable{ private GoodsService goodsService; public SetThread(GoodsService goodsService) { this.goodsService = goodsService; } @Override public void run() { long start = System.currentTimeMillis(); Random r = new Random(); //操作10次 for(int i=0;i<10;i++){ SleepTools.ms(50); goodsService.setNum(r.nextInt(10)); } System.out.println(Thread.currentThread().getName() +"写商品数据耗时:"+(System.currentTimeMillis()-start)+"ms---------"); } } public static void main(String[] args) throws InterruptedException { GoodsInfo goodsInfo = new GoodsInfo("Cup",100000,10000); // 使用Sync锁 GoodsService goodsService = new UseSyn(goodsInfo); // 启动三个写线程 for(int i = 0;i<minthreadCount;i++){ Thread setT = new Thread(new SetThread(goodsService)); // 每启动一个写线程,就启动10个读线程 for(int j=0;j<readWriteRatio;j++) { Thread getT = new Thread(new GetThread(goodsService)); getT.start(); } SleepTools.ms(100); setT.start(); } } }
执行结果:
Thread-0写商品数据耗时:3803ms--------- Thread-11写商品数据耗时:3912ms--------- Thread-22写商品数据耗时:4629ms--------- Thread-30读取商品数据耗时:10352ms Thread-29读取商品数据耗时:12166ms Thread-19读取商品数据耗时:13184ms Thread-6读取商品数据耗时:13583ms Thread-21读取商品数据耗时:13801ms Thread-18读取商品数据耗时:13832ms Thread-15读取商品数据耗时:13907ms Thread-32读取商品数据耗时:14016ms Thread-28读取商品数据耗时:14022ms Thread-23读取商品数据耗时:14063ms Thread-27读取商品数据耗时:14162ms Thread-13读取商品数据耗时:14323ms Thread-8读取商品数据耗时:14462ms Thread-4读取商品数据耗时:14614ms Thread-3读取商品数据耗时:14619ms Thread-5读取商品数据耗时:14676ms Thread-24读取商品数据耗时:14693ms Thread-25读取商品数据耗时:14698ms Thread-10读取商品数据耗时:14979ms Thread-7读取商品数据耗时:15026ms Thread-14读取商品数据耗时:14955ms Thread-26读取商品数据耗时:14923ms Thread-12读取商品数据耗时:15112ms Thread-17读取商品数据耗时:15174ms Thread-31读取商品数据耗时:15106ms Thread-20读取商品数据耗时:15210ms Thread-9读取商品数据耗时:15340ms Thread-16读取商品数据耗时:15305ms Thread-2读取商品数据耗时:15735ms Thread-1读取商品数据耗时:15835ms
通过执行结果可以清晰的看到,居然花费了10多秒,而且是越后面的线程花费的时间就越长
接下来切换读写锁
package org.dance.day4.rw; import org.dance.tools.SleepTools; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * 类说明:使用读写锁 实现读写分离 */ public class UseRwLock implements GoodsService { private GoodsInfo goodsInfo; /** * 创建读写锁,默认使用非公平锁 */ private final ReadWriteLock lock = new ReentrantReadWriteLock(); /** * 获取读锁 */ private final Lock readLock = lock.readLock(); /** * 获取写锁 */ private final Lock writeLock = lock.writeLock(); public UseRwLock(GoodsInfo goodsInfo) { this.goodsInfo = goodsInfo; } @Override public GoodsInfo getNum() { // 添加读锁 readLock.lock(); try { SleepTools.ms(5); return this.goodsInfo; } finally { readLock.unlock(); } } @Override public void setNum(int number) { // 添加写锁 writeLock.lock(); try { SleepTools.ms(5); goodsInfo.changeNumber(number); } finally { writeLock.unlock(); } } }
业务调用代码修改,只修该这一行就可以
// 使用Sync锁 // GoodsService goodsService = new UseSyn(goodsInfo); // 使用读写锁 GoodsService goodsService = new UseRwLock(goodsInfo);
执行结果:
Thread-1读取商品数据耗时:616ms Thread-6读取商品数据耗时:623ms Thread-8读取商品数据耗时:623ms Thread-3读取商品数据耗时:624ms Thread-10读取商品数据耗时:622ms Thread-4读取商品数据耗时:624ms Thread-7读取商品数据耗时:623ms Thread-5读取商品数据耗时:623ms Thread-2读取商品数据耗时:626ms Thread-9读取商品数据耗时:622ms Thread-0写商品数据耗时:576ms--------- Thread-13读取商品数据耗时:649ms Thread-12读取商品数据耗时:650ms Thread-19读取商品数据耗时:654ms Thread-14读取商品数据耗时:654ms Thread-17读取商品数据耗时:654ms Thread-20读取商品数据耗时:655ms Thread-21读取商品数据耗时:655ms Thread-15读取商品数据耗时:654ms Thread-16读取商品数据耗时:654ms Thread-18读取商品数据耗时:654ms Thread-11写商品数据耗时:571ms--------- Thread-22写商品数据耗时:575ms--------- Thread-26读取商品数据耗时:671ms Thread-30读取商品数据耗时:670ms Thread-28读取商品数据耗时:670ms Thread-25读取商品数据耗时:671ms Thread-29读取商品数据耗时:671ms Thread-24读取商品数据耗时:672ms Thread-32读取商品数据耗时:671ms Thread-31读取商品数据耗时:671ms Thread-27读取商品数据耗时:671ms Thread-23读取商品数据耗时:672ms
根据执行结果可以看出,读写锁,在读写分离时使用,相对于Synchronized排他锁来说,性能提升了10倍不止,所以在读多写少的时候,推荐使用读写锁
作者:彼岸舞
时间:2020\11\03
内容关于:并发编程
本文来源于网络,只做技术分享,一概不负任何责任
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)
· AI 智能体引爆开源社区「GitHub 热点速览」