(9)读写锁ReentrantReadWriteLock解析

一、 ReentrantReadWriteLock简介

    ReentrantReadWriteLock是在AQS的基础上实现的一个可重入锁。它的内部维护了一把读锁和一把写锁,读锁是共享锁,写锁是排他锁。这样就保证了写数据时的线程安全性,又保证了读数据时的多线程并发,比较适合读取数据较多而写数据较少的并发场景。

    写一个例子演示ReentrantReadWriteLock的使用。

class RWData<T>{
    private T data ;
    private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(true);
    private ReentrantReadWriteLock.ReadLock readLock = rwl.readLock();
    private ReentrantReadWriteLock.WriteLock writeLock = rwl.writeLock();
    public RWData(){
    }

    public void put(T t){
        try{
            System.out.println("Before write lock");
            writeLock.lock();
            data = t;
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            writeLock.unlock();
            System.out.println("After write unlock");
        }
    }

    public T get() {
        try{
            readLock.lock();
            return data;
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            readLock.unlock();
        }
        return null;
    }
}



public class TestRWLock {
    public static void main(String[] args) {
        RWData<Integer> queue = new RWData<>();
        for (int i = 0; i < 10; i++){
            new Thread(()->{
                while(true){
                    Integer data = queue.get();
                    if (null != data)
                        System.out.println(Thread.currentThread().getName() + "gets data " + data.intValue());
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
             }).start();
        }
        
    new Thread(()->{
            while(true){
                int data = new Random().nextInt(1000);
                queue.put(data);
                System.out.println(Thread.currentThread().getName() + "puts data " + data);
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}

  运行结果

   写的时候其他的线程都需要等待,当读的时候所有的线程可以同时读。

   写锁是独占的,写时不能有其他线程写也不能读;所有的独锁都释放完之前也不能加写锁。这将在代码中有体现。

 

二、ReentrantReadWriteLock源码解析

    一个读写锁,需要维护3个数据:

  •     写锁的重入次数
  •     读锁的个数(读锁有多个)
  •     读锁的重入次数

Sync继承AQS的state,高16位存读的,低16位存写的,Sync类的readHolds存储了每一个读锁的重入次数。

通过getState与setState来获取和保存。

    ReentrantReadWriteLock内部有一个ReadLock 类和一个WriteLock类,各包含了一个Sync子类实例负责具体干活。Sync类extends AQS实现了大多数功能,但它是abstract类,需要实例化子类FairSync和NonfairSync来干活。它们分别实现了公平方式和非公平方式。

    创建ReentrantReadWriteLock对象时要选择公平还是非公平,公平就用FairSync,非公平就用NonfairSync,默认非公平。

 

  •    ReadLock

   ReadLock的lock调用了内部的sync实例的acquireShared()

 acquireShared调用了ReadLock中存的sync实例的tryAcquireShared()

  unlock则调用了 --> sync.releaseShared()  --> Sync子类实例的 tryReleaseShared。    

 

  • WriteLock

  writeLock的lock调用了--> sync.acquire()  --> Sync子类实例的tryAcquire

  unlock调用了--> sync.acquire()  --> Sync子类实例的tryAcquire

这些代码的意义与上一节 (7)AbstractQueuedSynchronizer和ReentrantLock—— 可重入锁的实现  基本一致,无需赘述。

 

 

     writerShouldBlock看是否需要使其他的线程阻塞。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章