并发包中的ReadWriteLock读写锁

一、ReadWriteLock读写锁

读写锁ReadWriteLock,是JDK1.5 中提供的读写分离锁,读写分离锁可以有效地帮助减少锁竞争,以提升系统性能。


ReadWriteLock维护了一对相关的锁,一个用于只读操作,另一个用于写入操作。只要没有 writer,读取锁可以由多个 reader 线程同时保持。写入锁是独占的


接口 描述
Lock readLock() 返回用于读取操作的锁
Lock writeLock() 返回用于写入操作的锁。

1.1 案例

案例,三个写线程,三个读线程


/**
 *ReentrantReadWriteLock  读写互斥,
 *如果读操作被上锁,写操作就不能进行,
 *如果写操作被上锁,读操作就不能进行,
 *
 *读操作上锁后,需要解锁后, 写才能上锁。
 * 如果读没有解锁,还调用了写的锁,就会造成堵塞,让线程卡在哪里。
 * 反之却是可以的,即在写没有解锁,读操作上锁是可以的。(叫做降级锁)
 * 
 */
public class ReadWriteLockTest {

    public static void main(String[] args) {
        final Queue q3 = new Queue();
        // 弄3个读的线程, 弄3个写的线程
        for (int i = 1; i <= 3; i++) {
            new Thread() {
                public void run() {
                    while (true) {
                        q3.put(new Random().nextInt(10000));
                    }
                }
            }.start();

            new Thread() {
                public void run() {
                    while (true) {
                        q3.get();
                    }
                }
            }.start();
        }
    }
}

class Queue {
    private Object data = null;
    // hibernate load的方法实现就是 ReentrantReadWriteLock 放在代理对象中
    private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();

    public void get() {
        rwl.readLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + "开始取");
            Thread.sleep((long) (Math.random() * 1000));
            System.out.println(Thread.currentThread().getName() + "取完毕" + data);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        rwl.readLock().unlock();
    }

    public void put(Object obj) {
        rwl.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + "开始写");
            Thread.sleep((long) (Math.random() * 1000));
            this.data = obj;
            System.out.println(Thread.currentThread().getName() + "写结束" + obj);

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        rwl.writeLock().unlock();
    }
}

1.2 读写锁实现缓存

下面的代码展示了如何利用重入来执行升级缓存后的锁降级(伪代码)

class CachedData {
   Object data;
   volatile boolean cacheValid;
   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();
        // 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();
        rwl.writeLock().unlock(); // Unlock write, still hold read
     }
     use(data);
     rwl.readLock().unlock();
   }
 }

在使用某些种类的 Collection时,可以使用 ReentrantReadWriteLock 来提高并发性。通常,在预期 collection 很大,读取者线程访问它的次数多于写入者线程,并且 entail 操作的开销高于同步开销时,这很值得一试。例如,以下是一个使用 TreeMap 的类,预期它很大,并且能被同时访问。

class RWDictionary {
    private final Map<String, Data> m = new TreeMap<String, Data>();
    private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
    private final Lock r = rwl.readLock();
    private final Lock w = rwl.writeLock();

    public Data get(String key) {
        r.lock();
        try { return m.get(key); }
        finally { r.unlock(); }
    }
    public String[] allKeys() {
        r.lock();
        try { return m.keySet().toArray(); }
        finally { r.unlock(); }
    }
    public Data put(String key, Data value) {
        w.lock();
        try { return m.put(key, value); }
        finally { w.unlock(); }
    }
    public void clear() {
        w.lock();
        try { m.clear(); }
        finally { w.unlock(); }
    }
 }

模拟缓存

public class Cache {

    private Map<String, Object> cache = new HashMap<String, Object>();

    private ReadWriteLock rwl = new ReentrantReadWriteLock();
    public  Object getData(String key){
        rwl.readLock().lock();
        Object value = null;
        try{
            value = cache.get(key);
            if(value == null){
                rwl.readLock().unlock();
                rwl.writeLock().lock();
                try{
                    if(value==null){
                        value = "aaaa";//实际失去queryDB();
                    }
                }finally{
                    rwl.writeLock().unlock();
                }
                rwl.readLock().lock();
            }
        }finally{
            rwl.readLock().unlock();
        }
        return value;
    }
}

参考

尚硅谷多线程视频

猜你喜欢

转载自blog.csdn.net/qq_31156277/article/details/80601261