• 读写锁 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倍不止,所以在读多写少的时候,推荐使用读写锁

    作者:彼岸舞

    时间:2020113

    内容关于:并发编程

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

  • 相关阅读:
    C#操作ini配置文件和写入日志操作
    asp.net AJAX 定期刷新页面,然后,在 Timer 的事件中弹出窗口
    setInterval和setTimeout的区别
    检测远程URL是否存在
    SharePoint列表的模板类型中的BaseType参数和ListTemplate参数
    TCP中的Flag options
    jQuery基础教程摘录 Hello world
    SharePoint站点无法打开的问题
    SPQuery在引用field的时候要用internal name
    Windows Server 2008中用管理员的权限使用命令行来打开程序
  • 原文地址:https://www.cnblogs.com/flower-dance/p/13917327.html
Copyright © 2020-2023  润新知