JUC——读写锁

什么是读写锁

  • 现实中有这样一种场景:对共享资源有读和写的操作,且写操作没有读操作那么频繁。在没有写操作的时候,多个线程同时读一个资源没有任何问题,所以应该允许多个线程同时读取共享资源;但是如果一个线程想去写这些共享资源,就不应该允许其他线程对该资源进行读和写的操作了。
  • 读锁是共享锁,写锁是独占锁。一个资源可以被多个读线程访问,也可以被一个写线程访问,但是不能同时存在读写线程对资源的访问;读写互斥、读读共享。
  • 针对这种场景,JAVA 的并发包提供了读写锁 ReentrantReadWriteLock,它表示两个锁,一个是读操作相关的锁,称为共享锁;一个是写相关的锁,称为排他锁。
    在这里插入图片描述

线程进入读锁的前提条件:

  • 没有其他线程的写锁
  • 没有写请求, 或者有写请求,但调用线程和持有锁的线程是同一个(可重入锁)。

线程进入写锁的前提条件:

  • 没有其他线程的读锁
  • 没有其他线程的写锁
    而读写锁有以下三个重要的特性:
  • 公平选择性:支持非公平(默认)和公平的锁获取方式,吞吐量还是非公平优于公平。
  • 重进入:读锁和写锁都支持线程重进入。
  • 锁降级:遵循获取写锁、获取读锁再释放写锁的次序,写锁能够降级成为读锁。
    读写锁演变
    在这里插入图片描述
    读写锁降级
    在这里插入图片描述

入门案例
使用 ReentrantReadWriteLock 对一个 hashmap 进行读和写操作

package com.itguigu.JUC.readwrite;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * @Author 小杰 @Date 2022/5/7 20:49 @Version 1.0 读写锁入门案例: 使用 ReentrantReadWriteLock 对一个 hashmap
 * 进行读和写操作
 */
public class ReadWriteLock {
  public static void main(String[] args) {
    Mycache mycache = new Mycache();
    // 创建5线程进行写操作
    for (int i = 1; i <= 5; i++) {
      final int num = i;
      new Thread(
              () -> {
                mycache.put("" + num, "" + num);
              },
              String.valueOf(i))
          .start();
    }
    // 创建5线程进性读操作。
    for (int i = 1; i <= 5; i++) {
      final int num = i;
      new Thread(
              () -> {
                mycache.read("" + num);
              },
              String.valueOf(i))
          .start();
    }
  }
}

/** 资源类:map和读写操作。 */
class Mycache {
  Map<String, Object> map = new HashMap<>();
  // 创建读写锁对象
  private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
  /**
   * 写操作
   *
   * @param key
   * @param value
   */
  public void put(String key, Object value) {
    // 添加写锁
    readWriteLock.writeLock().lock();
    try {
      System.out.println(Thread.currentThread().getName() + "正在写操作...");
      map.put(key, value);
      TimeUnit.SECONDS.sleep(2);
      System.out.println(Thread.currentThread().getName() + "写完了...");
    } catch (InterruptedException e) {
      e.printStackTrace();
    } finally {
      // 释放锁
      readWriteLock.writeLock().unlock();
    }
  }

  /**
   * 读操作
   *
   * @param key
   * @return
   */
  public Object read(String key) {
    //上锁
    readWriteLock.readLock().lock();
    Object result = null;
    try {
      System.out.println(Thread.currentThread().getName() + "正在读操作---");
      result = map.get(key);
      TimeUnit.SECONDS.sleep(2);
      System.out.println(Thread.currentThread().getName() + "读取完了---");
    } catch (InterruptedException e) {
      e.printStackTrace();
    }finally{
      //释放锁
      readWriteLock.readLock().unlock();
    }
    return result;
  }
}

结果
在这里插入图片描述

小结

  • 在线程持有读锁的情况下,该线程不能取得写锁(因为获取写锁的时候,如果发现当前的读锁被占用,就马上获取失败,不管读锁是不是被当前线程持有)。 • 在线程持有写锁的情况下,该线程可以继续获取读锁(获取读锁时如果发现写锁被占用,只有写锁没有被当前线程占用的情况才会获取失败)。
  • 原因: 当线程获取读锁的时候,可能有其他线程同时也在持有读锁,因此不能把获取读锁的线程“升级”为写锁;而对于获得写锁的线程,它一定独占了读写锁,因此可以继续让它获取读锁,当它同时获取了写锁和读锁后,还可以先释放写锁继续持有读锁,这样一个写锁就“降级”为了读锁。
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值