通过ReentrantReadWriteLock做一个缓存工具

JDK1.5开始就出现了java.util.concurrent.locks.ReentrantReadWriteLock,将锁细分程了读锁和写锁,

  • 读锁:属于共享锁,多个线程可同时获取该锁,在读锁代码中获取不到写锁,只有读锁释放后才能获取到写锁
  • 写锁:属于独享锁,只有一个线程能获取到该锁并操作数据,在写锁中还可以获取到读锁(写锁的降级)

可以同通过这两种锁做一些缓存工具-代码如下:

package com.milla.study.netbase.expert.concurrent.lock;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * @Package: com.milla.study.netbase.expert.concurrent.lock
 * @Description: <>
 * @Author: MILLA
 * @CreateDate: 2020/6/1 14:19
 * @UpdateUser: MILLA
 * @UpdateDate: 2020/6/1 14:19
 * @UpdateRemark: <>
 * @Version: 1.0
 */
public class CacheDataTests {
    //读写锁
    ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    //读锁
    ReentrantReadWriteLock.ReadLock readLock = lock.readLock();
    //写锁
    ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock();
    //缓存数据
    Map<String, Object> data = new HashMap<>();
    static final ThreadPoolExecutor executor = new
            ThreadPoolExecutor(8, 12, 10,
            TimeUnit.SECONDS, new ArrayBlockingQueue<>(30), new ThreadPoolExecutor.CallerRunsPolicy());//如果线程池没有关闭直接运行任务(性能不高)

    public static void main(String[] args) {
        CacheDataTests cacheData = new CacheDataTests();
        for (int i = 0; i < 1000; i++) {
            Double random = Math.random() * 10;
            int key = random.intValue();
            executor.submit(() -> System.out.println(cacheData.cache(key + "")));
        }
        executor.shutdown();
    }
    //-从缓存中获取数据,如果不存在及从数据库或者其他地方获取数据,当前为模拟自动生成数据
    public Object cache(String key) {
        try {
            //加读锁-读锁属于共享锁可以支持多个线程获取锁,但此时排斥写锁
            readLock.lock();
            //如果有对应的key
            if (Objects.isNull(data.get(key))) {
                //解除读锁
                readLock.unlock();
                //添加写锁,保证写锁内操作只有一个线程能执行-避免该处过多线程操作导致崩溃或数据不一致
                writeLock.lock();
                try {
                    //双重判断,防止有其他线程已经处理,出现重复处理的情况
                    if (Objects.isNull(data.get(key))) {
                        //获取数据 TODO 需要进行的具体操作
                        String s = UUID.randomUUID().toString();
                        System.out.println("key: " + key + " value: " + s);
                        data.put(key, s);
                    }
                    //在没释放写锁前,加读锁,将其他线程的可能进行的写锁屏蔽-降级写锁处理,保证数据的一致性
                    readLock.lock();
                } finally {
                    //最终释放写锁-此时仍然存在读锁
                    writeLock.unlock();
                }
            }
            return data.get(key);
        } finally {
            //最终释放读锁
            readLock.unlock();
        }
    }
}

 

PS:不得不感叹大神们确实是奇思妙想,很多年前就给出了自己的独到见解。 工具类直接参照JDK,伪代码如下:

 

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();
      }
    }
  }
}

 

//也可以定义一个字典类
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(); }
    }
  }
}

 

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