排它锁:
之前的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
内容关于:并发编程
本文来源于网络,只做技术分享,一概不负任何责任