ReentrantReadWriteLock使用场景思考及验证

8 篇文章 3 订阅
7 篇文章 0 订阅

ReentrantReadWriteLock使用场景思考


最近一直在琢磨 ReentrantReadWriteLock的使用场景是什么,后来突然就想明白了,其实就是 读读并发、读写互斥、写写互斥而已。如果一个对象并发读的场景大于并发写的场景,那就可以使用 ReentrantReadWriteLock来达到保证线程安全的前提下提高并发效率。首先,我们先了解一下Doug Lea为我们准备的两个demo。

CachedData

一个缓存对象的使用案例,缓存对象在使用时,一般并发读的场景远远大于并发写的场景,所以缓存对象是非常适合使用ReentrantReadWriteLock来做控制的

class CachedData {
   //被缓存的具体对象
   Object data;
   //当前对象是否可用,使用volatile来保证可见性
   volatile boolean cacheValid;
   //今天的主角,ReentrantReadWriteLock 
   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.
         //经典处理之二,在独占锁内部要处理数据时,一定要做二次校验
         //因为可能同时有多个线程全都在获取写锁,
         //当时线程1释放写锁之后,线程2马上获取到写锁,此时如果不做二次校验那可能就导致某些操作做了多次
         if (!cacheValid) {
           data = ...
           //当缓存对象更新成功后,重置标记为true
           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();
     }
   }
 }

RWDictionary

Doug Lea给出的第二个demo,一个并发容器的demo。并发容器我们一般都是直接使用ConcurrentHashMap的,但是我们可以使用非并发安全的容器+ReentrantReadWriteLock来组合出一个并发容器。如果这个并发容器的读的频率>写的频率,那这个效率还是不错的

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

思考和验证

这两个demo其实在我目前的开发中基本上哪个也用不上,但是我能不能通过ReentrantReadWriteLock手写一个简单的缓存容器呢?缓存容器本质上非常接近RWDictionary 但是需要提供增量缓存的方法。

  1. 我们先定义一个缓存容器的接口:

    /**
     * 缓存容器
     *
     * @param <T>
     * @author wangxing
     */
    public interface ICacheContainer<T> {
    
        /**
         * 获取对象
         *
         * @param key
         * @return
         */
        T get(String key, Function<String, T> mappingFunction);
    
        /**
         * 移除对象
         *
         * @param key
         * @return
         */
        T remove(String key);
    }
    
  2. 使用ReentrantReadWriteLock+HashMap提供一个实现

    /**
     * 缓存容器
     *
     * @param <T>
     * @author wangxing
     */
    public class ReadWriteLockCacheContainer<T> implements ICacheContainer<T> {
    
        private final HashMap<String, T> cacheMap = new HashMap<>();
        private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    
        @Override
        public T get(String key, Function<String, T> mappingFunction) {
            //读之前先上读锁
            readWriteLock.readLock().lock();
            try {
                //判断map中是否已经有了
                boolean has = cacheMap.containsKey(key);
                if (!has) {
                    //没有数据,需要写,读锁释放
                    readWriteLock.readLock().unlock();
                    //加写锁,必须先释放读锁,不然这里直接GG
                    readWriteLock.writeLock().lock();
                    try {
                        //并发二重校验
                        has = cacheMap.containsKey(key);
                        if (!has) {
                            T data = mappingFunction.apply(key);
                            cacheMap.put(key, data);
                        }
                    } finally {
                        //这里要先降级,给自己占一个坑,防止当前线程释放写锁之后,
                        //马上有其他线程获取到写锁,并对对象修改
                        readWriteLock.readLock().lock();
                        //finally里面释放写锁
                        readWriteLock.writeLock().unlock();
                    }
                }
                //考虑到并发,这里一定要从HashMap中读,并且一定要加读锁
                return cacheMap.get(key);
            } finally {
                //finally里面释放写锁
                readWriteLock.readLock().unlock();
            }
        }
    
        @Override
        public T remove(String key) {
            readWriteLock.writeLock().lock();
            try {
                return cacheMap.remove(key);
            } finally {
                readWriteLock.writeLock().unlock();
            }
        }
    }
    
  3. 为了比较效率,我还提供了ConcurrentHashMap和synchronized+HashMap的两个实现

    /**
     * 缓存容器
     *
     * @param <T>
     * @author wangxing
     */
    public class ConcurrentHashMapCacheContainer<T> implements ICacheContainer<T> {
    
        private final ConcurrentHashMap<String, T> cacheMap = new ConcurrentHashMap<>();
    
        @Override
        public T get(String key, Function<String, T> mappingFunction) {
            return cacheMap.computeIfAbsent(key, mappingFunction);
        }
    
        @Override
        public T remove(String key) {
            return cacheMap.remove(key);
        }
    }
    
    /**
     * 缓存容器
     *
     * @param <T>
     * @author wangxing
     */
    public class HashMapCacheContainer<T> implements ICacheContainer<T> {
    
        private final HashMap<String, T> cacheMap = new HashMap<>();
    
        @Override
        public synchronized T get(String key, Function<String, T> mappingFunction) {
            return cacheMap.computeIfAbsent(key, mappingFunction);
        }
    
        @Override
        public synchronized T remove(String key) {
            return cacheMap.remove(key);
        }
    }
    
  4. 附一下StudentService,StudentDAO就展示,就是JdbcTemplate查库而已了

    @Service
    public class StudentService {
    
        private static final Logger logger = LoggerFactory.getLogger(StudentService.class);
    
        @Autowired
        private ConcurrentHashMapCacheContainer<StudentDO> concurrentHashMapCache;
    
        @Autowired
        private HashMapCacheContainer<StudentDO> hashMapCache;
    
        @Autowired
        private ReadWriteLockCacheContainer<StudentDO> readWriteLockCache;
    
        @Autowired
        private ReadWriteLockCacheContainer2<StudentDO> readWriteLockCache2;
    
        @Autowired
        private StudentDAO studentDAO;
    
    
        public StudentDO getByIdFromConcurrentHashMapCache(String id) {
            Assert.notNull(id, "id is null");
            return concurrentHashMapCache.get(id, studentDAO::getById);
        }
    
        public StudentDO getByIdFromHashMapCache(String id) {
            Assert.notNull(id, "id is null");
            return hashMapCache.get(id, studentDAO::getById);
        }
    
        public StudentDO getByIdFromReadWriteLockCache(String id) {
            Assert.notNull(id, "id is null");
            return readWriteLockCache.get(id, studentDAO::getById);
        }
    
        public StudentDO getByIdFromReadWriteLockCache2(String id) {
            Assert.notNull(id, "id is null");
            return readWriteLockCache2.get(id, studentDAO::getById);
        }
    }
    
  5. 接下来验证一下正确性

  6. 然后我们测试一下效率,使用jmh测试,这样显得更专业,先上测试代码:

    @BenchmarkMode({Mode.AverageTime})
    @OutputTimeUnit(TimeUnit.NANOSECONDS)
    @Warmup(time = 1, iterations = 1)
    @Measurement(iterations = 3)
    @Threads(10)
    @Fork(1)
    @State(Scope.Benchmark)
    public class CacheTest {
    
        StudentService studentService;
        ConfigurableApplicationContext applicationContext;
    
        public static void main(String[] args) throws RunnerException {
            Options opt = new OptionsBuilder()
                    .include(CacheTest.class.getSimpleName())
                    .result("CacheTest.json")
                    .resultFormat(ResultFormatType.JSON).build();
            new Runner(opt).run();
        }
    
        @Setup
        public void init() {
        	//jmh与SpringBoot结合,只需要在@Setup中初始化一下SpringBoot的context以及需要用到的bean就可以了
            applicationContext = SpringApplication.run(Main.class);
            studentService = applicationContext.getBean(StudentService.class);
        }
    
        @TearDown
        public void down() {
    	    //跑完了记得关SpringBoot
            applicationContext.close();
        }
    
        public String getRandomId() {
            Random random = new Random();
            return String.format("%03d", random.nextInt(9) + 1);
        }
    
        @Benchmark
        public StudentDO getByIdFromHashMapCache() {
            return studentService.getByIdFromHashMapCache(getRandomId());
        }
    
        @Benchmark
        public StudentDO getByIdFromConcurrentHashMapCache() {
            return studentService.getByIdFromConcurrentHashMapCache(getRandomId());
        }
    
        @Benchmark
        public StudentDO getByIdFromReadWriteLockCache() {
            return studentService.getByIdFromReadWriteLockCache(getRandomId());
        }
    }
    

    因为缓存的加载在预热时就跑完了,所以测试的结果是面向缓存数据读取的,结果如下:

    # JMH version: 1.33
    # VM version: JDK 1.8.0_261, Java HotSpot(TM) 64-Bit Server VM, 25.261-b12
    # VM invoker: C:\Program Files\Java\jdk1.8.0_261\jre\bin\java.exe
    # VM options: -javaagent:D:\jetbrains\IntelliJ IDEA 2021.2\lib\idea_rt.jar=62663:D:\jetbrains\IntelliJ IDEA 2021.2\bin -Dfile.encoding=UTF-8
    # Blackhole mode: full + dont-inline hint (default, use -Djmh.blackhole.autoDetect=true to auto-detect)
    # Warmup: 1 iterations, 1 s each
    # Measurement: 3 iterations, 10 s each
    # Timeout: 10 min per iteration
    # Threads: 10 threads, will synchronize iterations
    # Benchmark mode: Average time, time/op
    # Benchmark: com.example.juc.CacheTest.getByIdFromConcurrentHashMapCache
    
    # Run progress: 0.00% complete, ETA 00:01:33
    # Fork: 1 of 1
    # Warmup Iteration   1: 
      .   ____          _            __ _ _
     /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
    ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
     \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
      '  |____| .__|_| |_|_| |_\__, | / / / /
     =========|_|==============|___/=/_/_/_/
     :: Spring Boot ::                (v2.4.6)
    
    21:18:23.031 [com.example.juc.CacheTest.getByIdFromConcurrentHashMapCache-jmh-worker-9] INFO  o.s.b.SpringApplication - Starting application using Java 1.8.0_261 on DESKTOP-5R7UHQH with PID 14256 (started by w123x in D:\code\myCode)
    21:18:23.031 [com.example.juc.CacheTest.getByIdFromConcurrentHashMapCache-jmh-worker-9] INFO  o.s.b.SpringApplication - No active profile set, falling back to default profiles: default
    21:18:24.402 [com.example.juc.CacheTest.getByIdFromConcurrentHashMapCache-jmh-worker-9] INFO  o.s.b.w.e.t.TomcatWebServer - Tomcat initialized with port(s): 8080 (http)
    21:18:24.402 [com.example.juc.CacheTest.getByIdFromConcurrentHashMapCache-jmh-worker-9] INFO  o.a.c.h.Http11NioProtocol - Initializing ProtocolHandler ["http-nio-8080"]
    21:18:24.402 [com.example.juc.CacheTest.getByIdFromConcurrentHashMapCache-jmh-worker-9] INFO  o.a.c.c.StandardService - Starting service [Tomcat]
    21:18:24.402 [com.example.juc.CacheTest.getByIdFromConcurrentHashMapCache-jmh-worker-9] INFO  o.a.c.c.StandardEngine - Starting Servlet engine: [Apache Tomcat/9.0.46]
    21:18:24.496 [com.example.juc.CacheTest.getByIdFromConcurrentHashMapCache-jmh-worker-9] INFO  o.a.c.c.C.[.[.[/] - Initializing Spring embedded WebApplicationContext
    21:18:24.496 [com.example.juc.CacheTest.getByIdFromConcurrentHashMapCache-jmh-worker-9] INFO  o.s.b.w.s.c.ServletWebServerApplicationContext - Root WebApplicationContext: initialization completed in 1387 ms
    21:18:24.920 [com.example.juc.CacheTest.getByIdFromConcurrentHashMapCache-jmh-worker-9] INFO  o.a.c.h.Http11NioProtocol - Starting ProtocolHandler ["http-nio-8080"]
    21:18:24.936 [com.example.juc.CacheTest.getByIdFromConcurrentHashMapCache-jmh-worker-9] INFO  o.s.b.w.e.t.TomcatWebServer - Tomcat started on port(s): 8080 (http) with context path ''
    21:18:24.952 [com.example.juc.CacheTest.getByIdFromConcurrentHashMapCache-jmh-worker-9] INFO  o.s.b.SpringApplication - Started application in 2.467 seconds (JVM running for 3.165)
    21:18:24.952 [com.example.juc.CacheTest.getByIdFromConcurrentHashMapCache-jmh-worker-8] INFO  c.e.j.c.StudentDAO - execute query [select id,name,mathScore,languageScore,englishScore from student where id=?] [007]
    21:18:24.952 [com.example.juc.CacheTest.getByIdFromConcurrentHashMapCache-jmh-worker-6] INFO  c.e.j.c.StudentDAO - execute query [select id,name,mathScore,languageScore,englishScore from student where id=?] [002]
    21:18:24.952 [com.example.juc.CacheTest.getByIdFromConcurrentHashMapCache-jmh-worker-2] INFO  c.e.j.c.StudentDAO - execute query [select id,name,mathScore,languageScore,englishScore from student where id=?] [008]
    21:18:24.952 [com.example.juc.CacheTest.getByIdFromConcurrentHashMapCache-jmh-worker-1] INFO  c.e.j.c.StudentDAO - execute query [select id,name,mathScore,languageScore,englishScore from student where id=?] [006]
    21:18:24.952 [com.example.juc.CacheTest.getByIdFromConcurrentHashMapCache-jmh-worker-10] INFO  c.e.j.c.StudentDAO - execute query [select id,name,mathScore,languageScore,englishScore from student where id=?] [004]
    21:18:24.952 [com.example.juc.CacheTest.getByIdFromConcurrentHashMapCache-jmh-worker-9] INFO  c.e.j.c.StudentDAO - execute query [select id,name,mathScore,languageScore,englishScore from student where id=?] [009]
    21:18:24.967 [com.example.juc.CacheTest.getByIdFromConcurrentHashMapCache-jmh-worker-8] INFO  c.z.h.HikariDataSource - HikariPool-1 - Starting...
    21:18:25.123 [com.example.juc.CacheTest.getByIdFromConcurrentHashMapCache-jmh-worker-8] INFO  c.z.h.HikariDataSource - HikariPool-1 - Start completed.
    21:18:25.139 [com.example.juc.CacheTest.getByIdFromConcurrentHashMapCache-jmh-worker-8] INFO  c.e.j.c.StudentDAO - execute query [select id,name,mathScore,languageScore,englishScore from student where id=?] [003]
    21:18:25.139 [com.example.juc.CacheTest.getByIdFromConcurrentHashMapCache-jmh-worker-4] INFO  c.e.j.c.StudentDAO - execute query [select id,name,mathScore,languageScore,englishScore from student where id=?] [005]
    21:18:25.139 [com.example.juc.CacheTest.getByIdFromConcurrentHashMapCache-jmh-worker-1] INFO  c.e.j.c.StudentDAO - execute query [select id,name,mathScore,languageScore,englishScore from student where id=?] [001]
    4181.528 ±(99.9%) 459.784 ns/op
    Iteration   1: 1431.993 ±(99.9%) 32.759 ns/op
    Iteration   2: 1419.286 ±(99.9%) 5.837 ns/op
    Iteration   3: 21:18:56.344 [com.example.juc.CacheTest.getByIdFromConcurrentHashMapCache-jmh-worker-2] INFO  c.z.h.HikariDataSource - HikariPool-1 - Shutdown initiated...
    21:18:56.346 [com.example.juc.CacheTest.getByIdFromConcurrentHashMapCache-jmh-worker-2] INFO  c.z.h.HikariDataSource - HikariPool-1 - Shutdown completed.
    1392.772 ±(99.9%) 16.996 ns/op
    
    
    Result "com.example.juc.CacheTest.getByIdFromConcurrentHashMapCache":
      1414.684 ±(99.9%) 365.081 ns/op [Average]
      (min, avg, max) = (1392.772, 1414.684, 1431.993), stdev = 20.011
      CI (99.9%): [1049.603, 1779.764] (assumes normal distribution)
    
    
    # JMH version: 1.33
    # VM version: JDK 1.8.0_261, Java HotSpot(TM) 64-Bit Server VM, 25.261-b12
    # VM invoker: C:\Program Files\Java\jdk1.8.0_261\jre\bin\java.exe
    # VM options: -javaagent:D:\jetbrains\IntelliJ IDEA 2021.2\lib\idea_rt.jar=62663:D:\jetbrains\IntelliJ IDEA 2021.2\bin -Dfile.encoding=UTF-8
    # Blackhole mode: full + dont-inline hint (default, use -Djmh.blackhole.autoDetect=true to auto-detect)
    # Warmup: 1 iterations, 1 s each
    # Measurement: 3 iterations, 10 s each
    # Timeout: 10 min per iteration
    # Threads: 10 threads, will synchronize iterations
    # Benchmark mode: Average time, time/op
    # Benchmark: com.example.juc.CacheTest.getByIdFromHashMapCache
    
    # Run progress: 33.33% complete, ETA 00:01:12
    # Fork: 1 of 1
    # Warmup Iteration   1: 
      .   ____          _            __ _ _
     /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
    ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
     \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
      '  |____| .__|_| |_|_| |_\__, | / / / /
     =========|_|==============|___/=/_/_/_/
     :: Spring Boot ::                (v2.4.6)
    
    21:18:58.782 [com.example.juc.CacheTest.getByIdFromHashMapCache-jmh-worker-10] INFO  o.s.b.SpringApplication - Starting application using Java 1.8.0_261 on DESKTOP-5R7UHQH with PID 1464 (started by w123x in D:\code\myCode)
    21:18:58.782 [com.example.juc.CacheTest.getByIdFromHashMapCache-jmh-worker-10] INFO  o.s.b.SpringApplication - No active profile set, falling back to default profiles: default
    21:18:59.977 [com.example.juc.CacheTest.getByIdFromHashMapCache-jmh-worker-10] INFO  o.s.b.w.e.t.TomcatWebServer - Tomcat initialized with port(s): 8080 (http)
    21:18:59.977 [com.example.juc.CacheTest.getByIdFromHashMapCache-jmh-worker-10] INFO  o.a.c.h.Http11NioProtocol - Initializing ProtocolHandler ["http-nio-8080"]
    21:18:59.977 [com.example.juc.CacheTest.getByIdFromHashMapCache-jmh-worker-10] INFO  o.a.c.c.StandardService - Starting service [Tomcat]
    21:18:59.977 [com.example.juc.CacheTest.getByIdFromHashMapCache-jmh-worker-10] INFO  o.a.c.c.StandardEngine - Starting Servlet engine: [Apache Tomcat/9.0.46]
    21:19:00.071 [com.example.juc.CacheTest.getByIdFromHashMapCache-jmh-worker-10] INFO  o.a.c.c.C.[.[.[/] - Initializing Spring embedded WebApplicationContext
    21:19:00.071 [com.example.juc.CacheTest.getByIdFromHashMapCache-jmh-worker-10] INFO  o.s.b.w.s.c.ServletWebServerApplicationContext - Root WebApplicationContext: initialization completed in 1227 ms
    21:19:00.496 [com.example.juc.CacheTest.getByIdFromHashMapCache-jmh-worker-10] INFO  o.a.c.h.Http11NioProtocol - Starting ProtocolHandler ["http-nio-8080"]
    21:19:00.512 [com.example.juc.CacheTest.getByIdFromHashMapCache-jmh-worker-10] INFO  o.s.b.w.e.t.TomcatWebServer - Tomcat started on port(s): 8080 (http) with context path ''
    21:19:00.512 [com.example.juc.CacheTest.getByIdFromHashMapCache-jmh-worker-10] INFO  o.s.b.SpringApplication - Started application in 2.181 seconds (JVM running for 2.68)
    21:19:00.528 [com.example.juc.CacheTest.getByIdFromHashMapCache-jmh-worker-8] INFO  c.e.j.c.StudentDAO - execute query [select id,name,mathScore,languageScore,englishScore from student where id=?] [004]
    21:19:00.528 [com.example.juc.CacheTest.getByIdFromHashMapCache-jmh-worker-8] INFO  c.z.h.HikariDataSource - HikariPool-1 - Starting...
    21:19:00.684 [com.example.juc.CacheTest.getByIdFromHashMapCache-jmh-worker-8] INFO  c.z.h.HikariDataSource - HikariPool-1 - Start completed.
    21:19:00.700 [com.example.juc.CacheTest.getByIdFromHashMapCache-jmh-worker-2] INFO  c.e.j.c.StudentDAO - execute query [select id,name,mathScore,languageScore,englishScore from student where id=?] [003]
    21:19:00.700 [com.example.juc.CacheTest.getByIdFromHashMapCache-jmh-worker-10] INFO  c.e.j.c.StudentDAO - execute query [select id,name,mathScore,languageScore,englishScore from student where id=?] [008]
    21:19:00.700 [com.example.juc.CacheTest.getByIdFromHashMapCache-jmh-worker-4] INFO  c.e.j.c.StudentDAO - execute query [select id,name,mathScore,languageScore,englishScore from student where id=?] [001]
    21:19:00.715 [com.example.juc.CacheTest.getByIdFromHashMapCache-jmh-worker-4] INFO  c.e.j.c.StudentDAO - execute query [select id,name,mathScore,languageScore,englishScore from student where id=?] [006]
    21:19:00.715 [com.example.juc.CacheTest.getByIdFromHashMapCache-jmh-worker-6] INFO  c.e.j.c.StudentDAO - execute query [select id,name,mathScore,languageScore,englishScore from student where id=?] [009]
    21:19:00.715 [com.example.juc.CacheTest.getByIdFromHashMapCache-jmh-worker-9] INFO  c.e.j.c.StudentDAO - execute query [select id,name,mathScore,languageScore,englishScore from student where id=?] [002]
    21:19:00.715 [com.example.juc.CacheTest.getByIdFromHashMapCache-jmh-worker-7] INFO  c.e.j.c.StudentDAO - execute query [select id,name,mathScore,languageScore,englishScore from student where id=?] [007]
    21:19:00.715 [com.example.juc.CacheTest.getByIdFromHashMapCache-jmh-worker-9] INFO  c.e.j.c.StudentDAO - execute query [select id,name,mathScore,languageScore,englishScore from student where id=?] [005]
    6498.052 ±(99.9%) 695.987 ns/op
    Iteration   1: 3199.530 ±(99.9%) 41.182 ns/op
    Iteration   2: 3273.503 ±(99.9%) 45.500 ns/op
    Iteration   3: 21:19:31.878 [com.example.juc.CacheTest.getByIdFromHashMapCache-jmh-worker-1] INFO  c.z.h.HikariDataSource - HikariPool-1 - Shutdown initiated...
    21:19:31.885 [com.example.juc.CacheTest.getByIdFromHashMapCache-jmh-worker-1] INFO  c.z.h.HikariDataSource - HikariPool-1 - Shutdown completed.
    3249.716 ±(99.9%) 48.810 ns/op
    
    
    Result "com.example.juc.CacheTest.getByIdFromHashMapCache":
      3240.916 ±(99.9%) 688.945 ns/op [Average]
      (min, avg, max) = (3199.530, 3240.916, 3273.503), stdev = 37.763
      CI (99.9%): [2551.972, 3929.861] (assumes normal distribution)
    
    
    # JMH version: 1.33
    # VM version: JDK 1.8.0_261, Java HotSpot(TM) 64-Bit Server VM, 25.261-b12
    # VM invoker: C:\Program Files\Java\jdk1.8.0_261\jre\bin\java.exe
    # VM options: -javaagent:D:\jetbrains\IntelliJ IDEA 2021.2\lib\idea_rt.jar=62663:D:\jetbrains\IntelliJ IDEA 2021.2\bin -Dfile.encoding=UTF-8
    # Blackhole mode: full + dont-inline hint (default, use -Djmh.blackhole.autoDetect=true to auto-detect)
    # Warmup: 1 iterations, 1 s each
    # Measurement: 3 iterations, 10 s each
    # Timeout: 10 min per iteration
    # Threads: 10 threads, will synchronize iterations
    # Benchmark mode: Average time, time/op
    # Benchmark: com.example.juc.CacheTest.getByIdFromReadWriteLockCache
    
    # Run progress: 66.67% complete, ETA 00:00:35
    # Fork: 1 of 1
    # Warmup Iteration   1: 
      .   ____          _            __ _ _
     /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
    ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
     \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
      '  |____| .__|_| |_|_| |_\__, | / / / /
     =========|_|==============|___/=/_/_/_/
     :: Spring Boot ::                (v2.4.6)
    
    21:19:34.335 [com.example.juc.CacheTest.getByIdFromReadWriteLockCache-jmh-worker-10] INFO  o.s.b.SpringApplication - Starting application using Java 1.8.0_261 on DESKTOP-5R7UHQH with PID 2572 (started by w123x in D:\code\myCode)
    21:19:34.335 [com.example.juc.CacheTest.getByIdFromReadWriteLockCache-jmh-worker-10] INFO  o.s.b.SpringApplication - No active profile set, falling back to default profiles: default
    21:19:35.546 [com.example.juc.CacheTest.getByIdFromReadWriteLockCache-jmh-worker-10] INFO  o.s.b.w.e.t.TomcatWebServer - Tomcat initialized with port(s): 8080 (http)
    21:19:35.546 [com.example.juc.CacheTest.getByIdFromReadWriteLockCache-jmh-worker-10] INFO  o.a.c.h.Http11NioProtocol - Initializing ProtocolHandler ["http-nio-8080"]
    21:19:35.546 [com.example.juc.CacheTest.getByIdFromReadWriteLockCache-jmh-worker-10] INFO  o.a.c.c.StandardService - Starting service [Tomcat]
    21:19:35.546 [com.example.juc.CacheTest.getByIdFromReadWriteLockCache-jmh-worker-10] INFO  o.a.c.c.StandardEngine - Starting Servlet engine: [Apache Tomcat/9.0.46]
    21:19:35.624 [com.example.juc.CacheTest.getByIdFromReadWriteLockCache-jmh-worker-10] INFO  o.a.c.c.C.[.[.[/] - Initializing Spring embedded WebApplicationContext
    21:19:35.624 [com.example.juc.CacheTest.getByIdFromReadWriteLockCache-jmh-worker-10] INFO  o.s.b.w.s.c.ServletWebServerApplicationContext - Root WebApplicationContext: initialization completed in 1211 ms
    21:19:36.081 [com.example.juc.CacheTest.getByIdFromReadWriteLockCache-jmh-worker-10] INFO  o.a.c.h.Http11NioProtocol - Starting ProtocolHandler ["http-nio-8080"]
    21:19:36.097 [com.example.juc.CacheTest.getByIdFromReadWriteLockCache-jmh-worker-10] INFO  o.s.b.w.e.t.TomcatWebServer - Tomcat started on port(s): 8080 (http) with context path ''
    21:19:36.112 [com.example.juc.CacheTest.getByIdFromReadWriteLockCache-jmh-worker-10] INFO  o.s.b.SpringApplication - Started application in 2.224 seconds (JVM running for 2.748)
    21:19:36.112 [com.example.juc.CacheTest.getByIdFromReadWriteLockCache-jmh-worker-2] INFO  c.e.j.c.StudentDAO - execute query [select id,name,mathScore,languageScore,englishScore from student where id=?] [005]
    21:19:36.128 [com.example.juc.CacheTest.getByIdFromReadWriteLockCache-jmh-worker-2] INFO  c.z.h.HikariDataSource - HikariPool-1 - Starting...
    21:19:36.268 [com.example.juc.CacheTest.getByIdFromReadWriteLockCache-jmh-worker-2] INFO  c.z.h.HikariDataSource - HikariPool-1 - Start completed.
    21:19:36.284 [com.example.juc.CacheTest.getByIdFromReadWriteLockCache-jmh-worker-9] INFO  c.e.j.c.StudentDAO - execute query [select id,name,mathScore,languageScore,englishScore from student where id=?] [001]
    21:19:36.300 [com.example.juc.CacheTest.getByIdFromReadWriteLockCache-jmh-worker-1] INFO  c.e.j.c.StudentDAO - execute query [select id,name,mathScore,languageScore,englishScore from student where id=?] [002]
    21:19:36.300 [com.example.juc.CacheTest.getByIdFromReadWriteLockCache-jmh-worker-1] INFO  c.e.j.c.StudentDAO - execute query [select id,name,mathScore,languageScore,englishScore from student where id=?] [003]
    21:19:36.300 [com.example.juc.CacheTest.getByIdFromReadWriteLockCache-jmh-worker-4] INFO  c.e.j.c.StudentDAO - execute query [select id,name,mathScore,languageScore,englishScore from student where id=?] [009]
    21:19:36.300 [com.example.juc.CacheTest.getByIdFromReadWriteLockCache-jmh-worker-7] INFO  c.e.j.c.StudentDAO - execute query [select id,name,mathScore,languageScore,englishScore from student where id=?] [007]
    21:19:36.300 [com.example.juc.CacheTest.getByIdFromReadWriteLockCache-jmh-worker-7] INFO  c.e.j.c.StudentDAO - execute query [select id,name,mathScore,languageScore,englishScore from student where id=?] [004]
    21:19:36.300 [com.example.juc.CacheTest.getByIdFromReadWriteLockCache-jmh-worker-3] INFO  c.e.j.c.StudentDAO - execute query [select id,name,mathScore,languageScore,englishScore from student where id=?] [008]
    21:19:36.300 [com.example.juc.CacheTest.getByIdFromReadWriteLockCache-jmh-worker-3] INFO  c.e.j.c.StudentDAO - execute query [select id,name,mathScore,languageScore,englishScore from student where id=?] [006]
    3981.655 ±(99.9%) 588.654 ns/op
    Iteration   1: 2042.329 ±(99.9%) 29.002 ns/op
    Iteration   2: 2053.372 ±(99.9%) 29.416 ns/op
    Iteration   3: 21:20:07.548 [com.example.juc.CacheTest.getByIdFromReadWriteLockCache-jmh-worker-7] INFO  c.z.h.HikariDataSource - HikariPool-1 - Shutdown initiated...
    21:20:07.555 [com.example.juc.CacheTest.getByIdFromReadWriteLockCache-jmh-worker-7] INFO  c.z.h.HikariDataSource - HikariPool-1 - Shutdown completed.
    2069.973 ±(99.9%) 25.902 ns/op
    
    
    Result "com.example.juc.CacheTest.getByIdFromReadWriteLockCache":
      2055.225 ±(99.9%) 253.855 ns/op [Average]
      (min, avg, max) = (2042.329, 2055.225, 2069.973), stdev = 13.915
      CI (99.9%): [1801.370, 2309.079] (assumes normal distribution)
    
    
    # Run complete. Total time: 00:01:47
    
    REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on
    why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial
    experiments, perform baseline and negative tests that provide experimental control, make sure
    the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts.
    Do not assume the numbers tell you what you want them to tell.
    
    Benchmark                                    Mode  Cnt     Score     Error  Units
    CacheTest.getByIdFromConcurrentHashMapCache  avgt    3  1414.684 ± 365.081  ns/op
    CacheTest.getByIdFromHashMapCache            avgt    3  3240.916 ± 688.945  ns/op
    CacheTest.getByIdFromReadWriteLockCache      avgt    3  2055.225 ± 253.855  ns/op
    

    从结果上能看出来,ConcurrentHashMap还是快的,但是我们通过ReentrantReadWriteLock实现的缓存在数据读取上也没慢太多,同时,因为是自己实现的,所以有更高的可扩展性

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 16
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值