JUC并发编程学习(九)-读写锁

读写锁

对共享资源进行读和写操作,且写的操作没有那么的频繁。在没有进行写的操作的时候,多个线程同时读取一个资源没有任何问题,所以可以同时允许多个线程读取共享资源。但是一个线程在进行写操作时,就不允许其他线程再对该资源进行读和写的操作了。

针对这种场景,JAVA的并发包提供了读写锁ReentrantReadWriteLock,它表示两个锁,一个是读操作相关的锁,称为共享锁;一个是写相关的锁,称为排他锁(独占锁),描述如下:
在这里插入图片描述

  • 线程进入读锁的前提条件
    没有其他线程的写锁,
    没有写请求或者有写请求,但调用线程和持有锁的线程是同一个。
  • 线程进入写锁的前提条件
    没有其他线程的读锁
    没有其他线程的写锁

读写锁有以下三个重要的特性

(1)公平选择性:支持非公平(默认)和公平的锁获取方式,吞吐量还是非公平优于公平。

(2)重进入:读锁和写锁都支持线程重进入。

(3)锁降级:遵循获取写锁、获取读锁再释放写锁的次序,写锁能够降级成为读锁。

案例分析
我们使用hashmap来进行读写操作,在未使用锁的情况,示例代码如下

package com.jp.readwriterlockDemo;


import java.util.HashMap;
import java.util.Map;


/**
* @className:
* @PackageName: com.jp.readwriterlockDemo
* @author: youjp
* @create: 2020-05-17 22:12
* @description:    TODO  读写锁demo
* @Version: 1.0
*/
public class ReadWriteLockDemo {


    public static void main(String[] args) {


        MyThread myThread=new MyThread();


        //先写入
        for (int i = 1; i <=5 ; i++) {
            final int temp=i;
            new Thread(()->{
                //写入
                myThread.wirte(String.valueOf(temp),temp);
            },String.valueOf(i)).start();
        }


        for (int i = 1; i <=5 ; i++) {
            final int temp=i;
            new Thread(()->{
                //读取
                myThread.read(String.valueOf(temp));
            },String.valueOf(i)).start();
        }
    }


}
//线程操作资源类
class MyThread{


    private volatile HashMap<String,Object> map=new HashMap<>();


    //读方法
    void read(String key){
        System.out.println("线程"+Thread.currentThread().getName()+"  key:"+key+"读取到"+map.get(key));
    }


    //写方法
    void wirte(String key,Object value){
        System.out.println("线程"+Thread.currentThread().getName()+" 开始写入key:"+key);
        map.put(key,value);
        System.out.println("线程"+Thread.currentThread().getName()+"写入key:"+key+" value:"+value+"成功");
    }
}

测试结果:
在这里插入图片描述
可以看到,我们在写入1的时候,还未写入一个完整的添加事件,便有一个写入4的线程插入。

使用读写锁后,线程之间就不会插队,一个线程必须等待其他线程完成才能执行。

ReadWriteLock编码模型:
1、创建读写锁

ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

可使用两种锁,写锁和读锁
在这里插入图片描述
2.加锁

lock.readLock().lock(); //读加锁
lock.writeLock().lock(); //写加锁

3.解锁

//相应的读锁或写锁调用解锁方法
lock.readLock().unlock();//读解锁
lock.writeLock().unlock();//写解锁

示例代码如下:

package com.jp.readwriterlockDemo;

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

/**
 * @className:
 * @PackageName: com.jp.readwriterlockDemo
 * @author: youjp
 * @create: 2020-05-17 22:12
 * @description:    TODO  读写锁demo
 * @Version: 1.0
 */
public class ReadWriteLockDemo {

    public static void main(String[] args) {

        MyThread myThread=new MyThread();

        //先写入
        for (int i = 1; i <=5 ; i++) {
            final int temp=i;
            new Thread(()->{
                //写入
                myThread.wirte(String.valueOf(temp),temp);
            },String.valueOf(i)).start();
        }

        for (int i = 1; i <=5 ; i++) {
            final int temp=i;
            new Thread(()->{
                //读取
                myThread.read(String.valueOf(temp));
            },String.valueOf(i)).start();
        }
    }

}
//线程操作资源类
class MyThread{

    private volatile HashMap<String,Object> map=new HashMap<>();
    private ReadWriteLock lock=new ReentrantReadWriteLock();//可重入读写锁

    //读方法
    void read(String key){
        lock.readLock().lock(); //读加锁
        System.out.println("线程"+Thread.currentThread().getName()+"  key:"+key+"读取到"+map.get(key));
        lock.readLock().unlock();//读解锁
    }

    //写方法
    void wirte(String key,Object value){
        lock.writeLock().lock(); //写加锁
        System.out.println("线程"+Thread.currentThread().getName()+" 开始写入key:"+key);
        map.put(key,value);
        System.out.println("线程"+Thread.currentThread().getName()+"写入key:"+key+" value:"+value+"成功");
        lock.writeLock().unlock();//写解锁
    }
}

执行结果:
在这里插入图片描述

源码解读:

public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializable {

    /** 读锁 */
    private final ReentrantReadWriteLock.ReadLock readerLock;

    /** 写锁 */
    private final ReentrantReadWriteLock.WriteLock writerLock;

    final Sync sync;
    
    /** 使用默认(非公平)的排序属性创建一个新的 ReentrantReadWriteLock */
    public ReentrantReadWriteLock() {
        this(false);
    }

    /** 使用给定的公平策略创建一个新的 ReentrantReadWriteLock */
    public ReentrantReadWriteLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
        readerLock = new ReadLock(this);
        writerLock = new WriteLock(this);
    }

    /** 返回用于写入操作的锁 */
    public ReentrantReadWriteLock.WriteLock writeLock() { return writerLock; }
    
    /** 返回用于读取操作的锁 */
    public ReentrantReadWriteLock.ReadLock  readLock()  { return readerLock; }


    abstract static class Sync extends AbstractQueuedSynchronizer {}

    static final class NonfairSync extends Sync {}

    static final class FairSync extends Sync {}

    public static class ReadLock implements Lock, java.io.Serializable {}

    public static class WriteLock implements Lock, java.io.Serializable {}
}

可以看到,ReentrantReadWriteLock实现了ReadWriteLock接口,ReadWriteLock接口定义了获取读锁和写锁的规范,具体需要实现类去实现;同时其还实现了Serializable接口,表示可以进行序列化,在源代码中可以看到ReentrantReadWriteLock实现了自己的序列化逻辑。

参考博文:
https://www.cnblogs.com/xiaoxi/p/9140541.html

有兴趣的老爷,可以关注我的公众号【一起收破烂】,回复【006】获取2021最新java面试资料以及简历模型120套哦~
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

收破烂的小熊猫~

你的鼓励将是我创造最大的东西~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值