并发编程中的读写锁ReentrantReadWriteLock

我们之前常用到的ReentrantLock和Synchorinized都是排他锁,意思都是同一时间只能有一个线程能够获取锁。

1 概念

     读写锁:表示同一时刻允许多个读线程并发访问,但是在写线程访问时,所有的读线程和写线程都被阻塞。和数据库的共享锁类似。

2.并发包中读写锁的的实现类ReentrantReadWriteLock

    2.1. 读写锁的实现ReentrantReadWriteLock,它是继承ReadWriteLock接口而不是Lock接口。

      ReentrantReadWriteLock特点

       

 2.2 ReentrantReadWriteLock有两个静态内部类 ,它是继承Lock接口的,主要用这两个是实现读写锁的实现   

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

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

2.3 ReentrantReadWriteLock 还提供一系列方法来展示工作状态的方法,当然不止这些,具体的可以参考Java API.
  
2.4 例子: 

package com.wusu.Cache;

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

public class Cache {
    static Map<String,Object> map = new HashMap<>();
    static ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
    
    static Lock  r = rwl.readLock();
    static Lock w = rwl.writeLock();
    
    public static final Object get(String key){
        r.lock();
        try {
            return map.get(key);  //获取key对应的value值
        }finally {
            r.unlock();
        }
    }

    public static final Object set(String key,Object value){
        w.lock();
        try {
            return map.put(key,value);  //设置key对应的value
        }finally {
            w.unlock();
        }
    }

    //清空map
    public static final void clear(){
        w.lock();
        try {
             map.clear();  //设置key对应的value
        }finally {
            w.unlock();
        }
    }
    
    
}

     如上所示:HashMap是线程不安全的,但是使用读写锁来保证Cache是线程安全的。这里的并发访问读,都可以获取读锁,除了写访问;对于访问写,必须获取写锁,使得其他读和写阻塞。这里用读写锁提高读操作的并发性,也保证了每次写操作对所有的读写操作的可见性。

3.读写锁的实现分析

 3.1 读写状态的设计

        读写锁依赖同步器(AQS)来实现,而读写状态就是其同步器的同步状态,这里的读写锁需要在同步状态上维护多个读线程和一个写线程。实现是通过把变量(32位 -int类型)“按位切割使用” 高16位表示读,低16位表示写。

3.2  写锁的获取和释放

     写锁writeLock 是支持重入锁的,表示当前线程获取了写锁,还可以增加写锁。如果当前线程在获取写锁时,读锁已经被获取了或者当前线程不是已经获取了写锁,那么就会处于等待状态。具体可以看源码的(tryAcquire方法)

    另外读锁如果存在,那么写锁就不能够被获取,原因是因为读写锁要确保写锁对读锁可见。

3.3读锁的获取和释放

      读锁也是支持重进入的共享锁,能够被多个线程同时获取,可以参考源码方法(tryAcquireShared)

3.4 锁降级

     锁降级指的是写锁降级成为读锁。解释:当前线程把持住写锁再获取到读锁,随后释放先前拥有的写锁的过程(如果当前进程获取到了写锁是允许继续获取读锁的)

     为了保证数据的可见性,如果当前线程不获取读锁而是直接释放写锁,假设此时有另一个线程T获取了写锁并修改了数据,那么当前线程无法感知线程T的数据更新,如果当前线程获取读锁,则线程T会被阻塞,直到当前线程使用数据并施放读锁之后,线程T才能获取写锁进行数据更新。

官网例子:

class CachedData {
   Object data;
   volatile boolean cacheValid;
   final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();

   void processCachedData() {
     rwl.readLock().lock();
     if (!cacheValid) {
        // Must release read lock before acquiring write lock
        rwl.readLock().unlock();
        rwl.writeLock().lock();
        try {
          // Recheck state because another thread might have
          // acquired write lock and changed state before we did.
          if (!cacheValid) {
            data = ...
            cacheValid = true;
          }
          // Downgrade by acquiring read lock before releasing write lock
          rwl.readLock().lock();
        } finally {
          rwl.writeLock().unlock(); // Unlock write, still hold read
        }
     }

     try {
       use(data);
     } finally {
       rwl.readLock().unlock();
     }
   }
 }

     参考网上给的实例网址:https://blog.csdn.net/u010512429/article/details/80011239

           用下面的代码来解释,若在第2行和第6行上并不加读锁的获取和释放(即//2和//6被注释掉),当前线程完成第1行的put操作后,进行第3行的写锁释放操作,若此时有另一个线程获得写锁并进行修改(即进行第1行操作),那么当前获得读锁的线程无法感知,在进行第4行和第5行操作时就会发生线程安全性问题。若加上第2行和第6行,由于读锁和写锁之间是互斥的,当写锁释放的时候,由于第2行读锁的存在,新的线程并不能获得写锁,此时就保证了线程安全性。

      例子:

private Map<String, Object> map = new HashMap<>();
 
	private ReadWriteLock rwl = new ReentrantReadWriteLock();
 
	private Lock r = rwl.readLock();
	private Lock w = rwl.writeLock();
 
	private volatile boolean isUpdate;
 
	public void readWrite() {
		r.lock(); // 为了保证isUpdate能够拿到最新的值
		if (!isUpdate) {
			r.unlock();
			w.lock();
			map.put("xxx", "xxx");//1
			r.lock();//2
			w.unlock();//3
		}
 
		Object obj = map.get("xxx");//4
 
		System.out.println(obj);//5
		r.unlock();//6
 
	}

     

猜你喜欢

转载自blog.csdn.net/weixin_40792878/article/details/81271731