ReadWriteLock可重入读写锁读写锁
什么是ReadWriteLock读写锁?
它是一个接口,只有一个实现类:ReentrantReadWriteLock(可重复的读写锁,也叫可重入锁)
ReadWriteLock维护一对关联的lock,一个用于只读操作,一个用于写入操作
读的时候可以多个线程同时读,写的时候只能被一个线程写
读锁又叫共享锁 一次性只能被一个线程占用 相当于一个原子性操作
写锁又叫独占锁 多个线程可以同时占有
代码测试,自定义缓存,首先是没有加锁的情况下
package com.wyh.ReadWriteLockTest; import java.util.HashMap; import java.util.Map; import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * @program: JUC * @description: 读写锁测试 * @author: 魏一鹤 * @createDate: 2022-02-19 21:56 **/ //ReadWriteLock是一个接口,只有一个实现类:ReentrantReadWriteLock(可重复的读写锁,也叫可重入锁) //ReadWriteLock维护一对关联的lock 一个用于只读操作,一个用于写入操作,读的时候可以多个线程同时读,写的时候只能被一个线程写 //自定义缓存 public class ReadWriterLockDemo { public static void main(String[] args){ //创建静态资源类 MyCache myCache = new MyCache(); //循环创建多线程 这几个线程只做读取的操作 for (int i = 1; i <= 5; i++) { //在lambda表达式中无法直接使用循环变量i 需要定义常量指明变量i做一个中间转换 //然后操作这个常量 final int temp=i; new Thread(()->{ myCache.put(temp+"",temp+""); }).start(); } //循环创建多线程 这几个线程只做存储缓存的操作 for (int i = 1; i <= 5; i++) { //在lambda表达式中无法直接使用循环变量i 需要定义常量指明变量i做一个中间转换 //然后操作这个常量 final int temp=i; new Thread(()->{ myCache.get(temp+""); }).start(); } } } //没加锁 class MyCache{ //缓存中一般都是键值对 通过get set 或者put来存或者取 //volatile保证原子性 private volatile Map<String,Object> map=new HashMap<>(); //存缓存 实际是一个写的过程 public void put(String key,Object value){ System.out.println(Thread.currentThread().getName()+"写入了"+key); //键值对存缓存 map.put(key,value); System.out.println(Thread.currentThread().getName()+"写入"+key+"完毕"); } //取缓存 实际是一个读的过程 public void get(String key){ System.out.println(Thread.currentThread().getName()+"读取"+key); //通过键取值 Object o = map.get(key); System.out.println(Thread.currentThread().getName()+"读取"+key+"完毕"); } }
Thread-0写入了1
Thread-4写入了5
Thread-4写入5完毕
Thread-3写入了4
Thread-1写入了2
Thread-2写入了3
Thread-2写入3完毕
Thread-1写入2完毕
Thread-3写入4完毕
Thread-0写入1完毕
Thread-5读取1
Thread-6读取2
Thread-5读取1完毕
Thread-7读取3
Thread-6读取2完毕
Thread-8读取4
Thread-8读取4完毕
Thread-7读取3完毕
Thread-9读取5
Thread-9读取5完毕
会发现数据比较混乱,没有出现我们想要的情况,一个线程插入的时候被其他线程插队了
然后是加锁后的代码 通过ReentrantReadWriteLock更加细粒度的控制,清楚的指明是读锁还是写锁
package com.wyh.ReadWriteLockTest; import java.util.HashMap; import java.util.Map; import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * @program: JUC * @description: 读写锁测试 * @author: 魏一鹤 * @createDate: 2022-02-19 21:56 **/ // 它主要有以下三个情况 // 1. 读取和读取 可以共存的 // 2. 写入和读取 不可以共存的 // 3. 写入和写入 不可以共存的 写的时候一次性只能进入一个线程,只有执行完毕才会释放锁 //ReadWriteLock是一个接口,只有一个实现类:ReentrantReadWriteLock(可重复的读写锁,也叫可重入锁) //ReadWriteLock维护一对关联的lock 一个用于只读操作,一个用于写入操作,读的时候可以多个线程同时读,写的时候只能被一个线程写 //自定义缓存 public class ReadWriterLockDemo { public static void main(String[] args){ //创建ReadWriteLock读写锁接口的实现类 可重入读写锁 ReentrantReadWriteLock readLock = new ReentrantReadWriteLock(); //创建静态资源类 MyCacheLock myCache = new MyCacheLock(); //循环创建多线程 这几个线程只做读取的操作 for (int i = 1; i <= 5; i++) { //在lambda表达式中无法直接使用循环变量i 需要定义常量指明变量i做一个中间转换 //然后操作这个常量 final int temp=i; new Thread(()->{ myCache.put(temp+"",temp+""); }).start(); } //循环创建多线程 这几个线程只做存储缓存的操作 for (int i = 1; i <= 5; i++) { //在lambda表达式中无法直接使用循环变量i 需要定义常量指明变量i做一个中间转换 //然后操作这个常量 final int temp=i; new Thread(()->{ myCache.get(temp+""); }).start(); } } } //没加锁 class MyCache{ //缓存中一般都是键值对 通过get set 或者put来存或者取 //volatile保证原子性 private volatile Map<String,Object> map=new HashMap<>(); //存缓存 实际是一个写的过程 public void put(String key,Object value){ System.out.println(Thread.currentThread().getName()+"写入了"+key); //键值对存缓存 map.put(key,value); System.out.println(Thread.currentThread().getName()+"写入"+key+"完毕"); } //取缓存 实际是一个读的过程 public void get(String key){ System.out.println(Thread.currentThread().getName()+"读取"+key); //通过键取值 Object o = map.get(key); System.out.println(Thread.currentThread().getName()+"读取"+key+"完毕"); } } //加锁 //读锁又叫共享锁 一次性只能被一个线程占用 相当于一个原子性操作 //写锁又叫独占锁 多个线程可以同时占有 class MyCacheLock{ //缓存中一般都是键值对 通过get set 或者put来存或者取 //volatile保证原子性 private volatile Map<String,Object> map=new HashMap<>(); //创建 读写锁(可重入锁) //它更加细粒度的控制 //相比于Lock 只能通过lock方法加锁 不能区分读锁还是写锁 但是ReentrantReadWriteLock可以清楚的区分加的是读锁还是写锁 private ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock(); //存缓存 实际是一个写的过程 写入的时候 只想同时被一个线程进行写入操作 是一个原子性操作 public void put(String key,Object value){ //ReentrantReadWriteLock的用法和Lock差不多 都要通过try catch finally,try中加锁并且写入业务代码 finally中解锁 try { //加一个写锁 并且加锁 = Lock lock=new Lock() lock.lock(); reentrantReadWriteLock.writeLock().lock(); System.out.println(Thread.currentThread().getName()+"写入了"+key); //键值对存缓存 map.put(key,value); System.out.println(Thread.currentThread().getName()+"写入"+key+"完毕"); } catch (Exception e) { e.printStackTrace(); } finally { //解锁 =lock.unLock(); reentrantReadWriteLock.writeLock().unlock(); } } //取缓存 实际是一个读的过程 读取的时候 想同时多个线程进行读取操作 所有线程都可以进行读取操作 public void get(String key){ //ReentrantReadWriteLock的用法和Lock差不多 都要通过try catch finally,try中加锁并且写入业务代码 finally中解锁 try { //加一个读锁 并且加锁 = Lock lock=new Lock() lock.lock(); reentrantReadWriteLock.readLock().lock(); System.out.println(Thread.currentThread().getName()+"读取"+key); //通过键取值 Object o = map.get(key); System.out.println(Thread.currentThread().getName()+"读取"+key+"完毕"); } catch (Exception e) { e.printStackTrace(); } finally { //解锁 =lock.unLock(); reentrantReadWriteLock.readLock().unlock(); } } }
Thread-0写入了1
Thread-0写入1完毕
Thread-1写入了2
Thread-1写入2完毕
Thread-2写入了3
Thread-2写入3完毕
Thread-3写入了4
Thread-3写入4完毕
Thread-4写入了5
Thread-4写入5完毕
Thread-7读取3
Thread-7读取3完毕
Thread-8读取4
Thread-8读取4完毕
Thread-9读取5
Thread-9读取5完毕
Thread-6读取2
Thread-6读取2完毕
Thread-5读取1
Thread-5读取1完毕
通过使用可重入读写锁发现,结果是我们想要的,读写分明,写的时候是原子性操作,不会被别的线程干涉
总结
使用其他主要是进行创建 然后使用它的读锁加锁/解锁,和写锁加锁/解锁,和Lock用法差不多,也是通过try catch finlly进行的,try中主要指名是什么锁(WriteLock/ReadLock)加锁(Lock)并且写入业务代码,finally中主要进行解锁,由于是手动的,不解锁(unLock)可能会造成死锁,主要代码如下所示
//创建 读写锁(可重入锁) //它更加细粒度的控制 //相比于Lock 只能通过lock方法加锁 不能区分读锁还是写锁 但是ReentrantReadWriteLock可以清楚的区分加的是读锁还是写锁 ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock(); //加一个写锁 并且加锁 = Lock lock=new Lock() lock.lock(); reentrantReadWriteLock.writeLock().lock(); //解锁 =lock.unLock(); reentrantReadWriteLock.writeLock().unlock(); //加一个读锁 并且加锁 = Lock lock=new Lock() lock.lock(); reentrantReadWriteLock.readLock().lock(); //解锁 =lock.unLock(); reentrantReadWriteLock.readLock().unlock();
它主要有以下三个情况
- 读取和读取 可以共存的
- 写入和读取 不可以共存的
- 写入和写入 不可以共存的 写的时候一次性只能进入一个线程,只有执行完毕才会释放锁