排他锁:synchronized和ReentrantLock都是排他锁,排他锁的意思就是在这一时刻只能有一个线程进入
读写锁:ReentrantReadWriteLock同一时刻允许多个读线程同时访问,但是写线程访问的时候,所有的读和写都被阻塞,最适宜与读多写少的情况
由于用户大部分的请求都是读,所以可以认为读写锁的效率比排他锁是质的飞跃
我们使用Synchronized实现一次和读写锁ReentrantReadWriteLock实现一次,来看效率对比
/**
*类说明:商品的实体类
*/
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;
}
}
/**
*类说明:商品的服务的接口
*/
public interface GoodsService {
public GoodsInfo getNum();//获得商品的信息
public void setNum(int number);//设置商品的数量
}
/**
*类说明:读写锁ReetrantReadWriteLock实现
*/
public class UseRwLock implements GoodsService {
private GoodsInfo goodsInfo;
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private final Lock getLock = lock.readLock();//读锁
private final Lock setLock = lock.writeLock();//写锁
public UseRwLock(GoodsInfo goodsInfo) {
this.goodsInfo = goodsInfo;
}
@Override
public GoodsInfo getNum() {
getLock.lock();
try {
SleepTools.ms(5);
return this.goodsInfo;
}finally {
getLock.unlock();
}
}
@Override
public void setNum(int number) {
setLock.lock();
try {
SleepTools.ms(5);
goodsInfo.changeNumber(number);
}finally {
setLock.unlock();
}
}
}
/**
*类说明:用排他锁来实现商品服务接口
*/
public class UseLock implements GoodsService {
private GoodsInfo goodsInfo;
private final Lock lock = new ReentrantLock();
public UseLock(GoodsInfo goodsInfo) {
this.goodsInfo = goodsInfo;
}
@Override
public GoodsInfo getNum() {
lock.lock();
try {
SleepTools.ms(5);
return this.goodsInfo;
}finally {
lock.unlock();
}
}
@Override
public void setNum(int number) {
lock.lock();
try {
SleepTools.ms(5);
goodsInfo.changeNumber(number);
}finally {
lock.unlock();
}
}
}
接下来我们的测试类
/**
*类说明:对商品进行业务的应用
*/
public class BusiApp {
static final int readWriteRatio = 10;//读写线程的比例
static final int minthreadCount = 3;//最少线程数
//读操作
private static class GetThread implements Runnable{
private GoodsService goodsService;
public GetThread(GoodsService goodsService) {
this.goodsService = goodsService;
}
@Override
public void run() {
long start = System.currentTimeMillis();
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();
for(int i=0;i<10;i++){//操作10次
SleepTools.ms(50);
goodsService.setNum(r.nextInt(10));
}
System.out.println(Thread.currentThread().getName()
+"写商品数据耗时:"+(System.currentTimeMillis()-start)+"ms---------");
}
}
public static void main(String[] args){
GoodsInfo goodsInfo = new GoodsInfo("Cup",100000,10000);
GoodsService goodsService =/* new UseRwLock(goodsInfo);*/new UseLock(goodsInfo);
for(int i = 0;i<minthreadCount;i++){
Thread setT = new Thread(new SetThread(goodsService));
for(int j=0;j<readWriteRatio;j++) {
Thread getT = new Thread(new GetThread(goodsService));
getT.start();
}
SleepTools.ms(100);
setT.start();
}
}
}
我们测试类可以通过注释new UseRwLock或new UseLock来切换使用什么方式,这里我们试着使用排他锁,输入大致如下
获得第一个锁时间为600多毫秒,可最后积累时间:
思路大家可以看代码来理解,这里不作详细解释,接下来我们试一下读写锁
综合计算下来性能差距大概20倍,如果业务数据量更多,效率可想而知