读锁有什么用?读为什么要加锁?

读为什么要加锁?

1、有些同学认为读锁没有用,他们的理由是:读操作又不会修改数据,想读就读呗,无论读的是就值还是新值,反正能读到。

2、也有同学认为读锁是为了防止多线程读到的数据不一致。

我认为不是这个原因,只需要问两个问题就知道了,首先问不一致的是什么?然后反问不一致会导致什么问题呢?

有些同学认为不一致就是有些线程读的是旧值,有些读的是新值,所以不一致。但是反问导致什么问题,就不是很好回答了,可能回答说为了保险吧,哈哈哈。

实际上即使加读锁,还是会存在有的线程读旧值,有的线程读新值,甚至非公平锁情况下,先开始的线程反而读到新值,而后开始的线程反而读到旧值,所以读锁并不是为了保证多线程读到的值是一样的。

3、那么读锁的作用是什么呢?

任何锁表面上是互斥,但本质是都是为了避免原子性问题(如果程序没有原子性问题,那只用volatile来避免可见性和有序性问题就可以了,效率更高),读锁自然也是为了避免原子性问题,比如一个long型参数的写操作并不是原子性的,如果允许同时读和写,那读到的数很可能是就是写操作的中间状态,比如刚写完前32位的中间状态。long型数都如此,而实际上一般读的都是复杂的对象,那中间状态的情况就更多了。

所以读锁是防止读到写的中间值

测试不加读锁

public class Demo {
    public static final int MAX_VALUE = 2;
    //值,且值不大于 MAX_VALUE
    long value;
    //返回值
    long get(){
        CommonMethod.sleep(10);
        return value;
    }
    //值加1,模拟非原子的写操作
    void add(){
        //加法结果类似写操作的中间状态
        value++;
        CommonMethod.sleep(10);
        //重置为0
        if (value > MAX_VALUE){
            value = 0;
        }
    }
    public static void main(String[] args) {
        TestMethod.test(new Demo());
    }
}

 
 

    公共代码:

    public class CommonMethod {
        private static AtomicLong UNIQ_ID = new AtomicLong();
        public static void sleep(long time){
            try {
                TimeUnit.MILLISECONDS.sleep(time);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        public static void log(String log){
            System.out.println(new SimpleDateFormat("yyyyMMdd HH:mm:ss.SSS").format(new Date())+ "  "+ Thread.currentThread().getName() + "  " + log);
        }
        public static void start(Collection<Thread> threads){
            threads.forEach(t->t.start());
        }
        public static void join(Thread thread){
            try {
                thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        public static void join(Collection<Thread> threads){
            threads.forEach(t-> join(t));
        }
        public static int randomInt(int high){
            return new Random().nextInt(high);
        }
        public static Long getUniqId(){
            return UNIQ_ID.getAndIncrement();
        }
    }
    
     
     

      测试程序如下:

      public class TestMethod {
          public static void test(Demo demo) {
              long start = System.currentTimeMillis();
              List<Thread> threads = new LinkedList<>();
              for (int i=0;i<10000;i++){
                  //读线程
                  threads.add(new Thread(() -> {
                                  long value = demo.get();
                                  if (value > Demo.MAX_VALUE){
                                      CommonMethod.log("读到错误的数据了");
                                      System.exit(-1);
                                  }
                                  CommonMethod.log("get " + value);
                              }, "thread-get-" + i));
                  //写线程
                  threads.add(new Thread(() -> demo.add() ,"thread-add-" + i));
              }
              CommonMethod.start(threads);
              CommonMethod.join(threads);
              CommonMethod.log(""+ demo.get());
              CommonMethod.log("耗时:"+(System.currentTimeMillis()-start));
          }
      }
      
       
       

        测试结果如下图:
        在这里插入图片描述

        使用读写锁

        /**
         * 读写锁示例
         */
        public class ReadWriteLockDemo extends Demo {
            private final ReadWriteLock rw = new ReentrantReadWriteLock();
            private final Lock rl = rw.readLock();
            private final Lock wl = rw.writeLock();
            @Override
            public long get(){
                rl.lock();
                try{
                    return super.get();
                }finally {
                    rl.unlock();
                }
            }
            @Override
            public void add(){
                wl.lock();
                try{
                    super.add();
                }finally{
                    wl.unlock();
                }
            }
            public static void main(String[] args) {
                TestMethod.test(new ReadWriteLockDemo());
            }
        }
        
         
         
        • 31

        测试结果:
        在这里插入图片描述

        测试环境:4核 8G

        • 5
          点赞
        • 25
          收藏
          觉得还不错? 一键收藏
        • 3
          评论

        “相关推荐”对你有帮助么?

        • 非常没帮助
        • 没帮助
        • 一般
        • 有帮助
        • 非常有帮助
        提交
        评论 3
        添加红包

        请填写红包祝福语或标题

        红包个数最小为10个

        红包金额最低5元

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

        抵扣说明:

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

        余额充值