读写锁 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

内容关于:并发编程

本文来源于网络,只做技术分享,一概不负任何责任

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值