• java自定义实现一个缓存器


    题目要求

    根据需求实现一个缓存池,当请求第一次加载的时候,计算缓存值,并存入缓存中,当另一请求来的时候,直接从缓存中获取对应值,避免重复计算,注意只允许第一次的请求进入计算过程:

    实现思路

    通过map实现缓存的功能,通过加锁的方式实现只有一个请求能够进入到计算的流程中

    • 缓存工具类
    package com.ijianghu.basetype.concurrent;
    
    import java.io.UnsupportedEncodingException;
    import java.security.MessageDigest;
    import java.security.NoSuchAlgorithmException;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Objects;
    import java.util.Set;
    import java.util.concurrent.ConcurrentHashMap;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     *
     * 设计一个缓存类,当多个线程调用的时候,第一个线程进行计算,并放入缓存;
     * 其他线程直接返回已有的缓存
     *
     */
    public class CacheUtils {
    
        /**
         * 缓存map
         */
        private HashMap<String,String> cacheMap = new HashMap();
    
        private ReentrantLock lock = new ReentrantLock();
    
    
        /**
         * 获取缓存值,只有一个线程能进来
         * @param key
         * @return
         */
        public String getCacheValue(String key){
    
            if(Objects.isNull(cacheMap.get(key))){
                lock.lock();
                if(Objects.isNull(cacheMap.get(key))){
                    String value = calculateValue(key);
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    cacheMap.put(key,value);
                    Set<Map.Entry<String, String>> entries = cacheMap.entrySet();
                    for (Map.Entry<String, String> entry: entries) {
                        System.out.println("key:".concat(entry.getKey()).concat("---------value:").concat(entry.getValue()));
                    }
                    lock.unlock();
                    return value;
                }
               else{
                    lock.unlock();
                    System.out.println(Thread.currentThread().getName().concat(key+"等待得到返回值:".concat(cacheMap.get(key))));
                    return cacheMap.get(key);
                }
    
            }else{
                System.out.println(Thread.currentThread().getName().concat("key+直接得到返回值:".concat(cacheMap.get(key))));
                return cacheMap.get(key);
            }
    
    
        }
    
        public String calculateValue(String key){
            try {
    
                MessageDigest digest = MessageDigest.getInstance("SHA-1");
                digest.update(key.getBytes("UTF-8"));
                byte[] digestByte = digest.digest();
                System.out.println("digestByte的长度:"+digestByte.length);
                String value = byteToHex(digestByte);
                System.out.println(Thread.currentThread().getName()+key+"计算缓存值:".concat(value));
                return value;
            } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            return null;
        }
    
        private String byteToHex(byte[] digestByte) {
            // 用来将字节转换成 16 进制表示的字符
            char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
            char str[]  = new char[16*2];
            int k = 0;//表示转换结果中对应的字符位置
            for(int i=0;i<16;i++){
                byte byte0 = digestByte[i];//取第i个字节
                str[k++] = hexDigits[byte0>>> 4 & 0xf];//取字节中高4位的数字转换,>>> 逻辑右移,将符号位一起右移;
                str[k++] = hexDigits[byte0 & 0xf];//取字节中低4位的数字转换
            }
    
            return new String(str);
        }
    
    }
    
    
    • 模拟调用 1

      在调用过程中,为了是主线程等待子线程先执行完,可以有多重实现方法,此类中包含了两种方式:

      1、使用CountDownLatch,使一个线程等待其他线程执行完,再执行;注意使用时,在执行过程中最后在调用countDown()方法

      2、调用线程的join()方法,使主线程进入等待状态(new 、runnable、blocked、waiting、time-waiting、terminated)

    package com.ijianghu.basetype.concurrent;
    
    import java.util.Objects;
    import java.util.concurrent.CountDownLatch;
    
    /**
     * @Description:create
     * @author: ext.liukai3
     * @date: 2021/6/2 11:08
     *
     * Thread.join()实现主线程阻塞
     *
     * 使用CountDownLatch实现主线程阻塞
     */
    public class CacheDemo {
    
        public static void main(String[] args) {
            CacheUtils cacheUtils = new CacheUtils();
            String[] str = new String[]{"a","b","c","d","e"};
            long start = System.currentTimeMillis();
            CountDownLatch countDownLatch = new CountDownLatch(100);
            System.out.println("start:"+start);
            for(int i=0;i<100;i++){
                Thread thread = new Thread(new CacheThread(str[i%5],cacheUtils,countDownLatch));
                try {
    
    
                    Thread.sleep(2);
                    thread.start();
    
    //                thread.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
    
    
            }
            try {
                countDownLatch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            long end = System.currentTimeMillis();
            System.out.println("end:"+end+"共消耗:"+(end-start));
        }
    
    
    }
    class CacheThread extends Thread{
        private CountDownLatch countDownLatch;
        private  CacheUtils cacheUtils;
        private String key;
        public CacheThread(String key,CacheUtils cacheUtils){
            this.key = key;
            this.cacheUtils = cacheUtils;
        }
    
        public CacheThread(String key,CacheUtils cacheUtils,CountDownLatch countDownLatch){
            this(key,cacheUtils);
            this.countDownLatch = countDownLatch;
        }
        @Override
        public void run() {
            cacheUtils.getCacheValue(key);
            if(Objects.nonNull(countDownLatch)){
                countDownLatch.countDown();
            }
        }
    }
    
    
    • 模拟调用2

      通过FutureTask.get()方法阻塞主线程

    /**
     */
    public class CacheFutureTask implements Callable<String> {
    
        private CacheUtils cacheUtils;
    
        private String key;
    
        public CacheFutureTask(String key,CacheUtils cacheUtils){
            this.cacheUtils = cacheUtils;
            this.key = key;
        }
    
        @Override
        public String call() throws Exception {
            String cacheValue = cacheUtils.getCacheValue(key);
            return cacheValue;
        }
    }
    
    /**
     *
     * FutureTask 使用get()可以实现主线程阻塞
     */
    public class CacheFutureDemo {
    
        public static void main(String[] args) {
            CacheUtils cacheUtils = new CacheUtils();
            String[] str = new String[]{"a","b","c","d","e"};
            long start = System.currentTimeMillis();
    
            for(int i=0;i<100;i++){
                CacheFutureTask futureTask = new CacheFutureTask(str[i % 5], cacheUtils);
                FutureTask<String> task = new FutureTask<>(futureTask);
                new Thread(task).start();
                String s = null;
                try {
                    s = task.get();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (ExecutionException e) {
                    e.printStackTrace();
                }
                System.out.println(s);
            }
            long end = System.currentTimeMillis();
            System.out.println("end:"+end+"共消耗:"+(end-start));
        }
    }
    
  • 相关阅读:
    《深入理解C#》泛型高级
    vs2019 插件下载慢的解决方法
    C# Tuple和 ValueTuple
    前端ajax用json方式请求 后端php 用 $GLOBALS['HTTP_RAW_POST_DATA'] 取值
    Vue之Axios跨域问题解决方案
    Jquery自定义方法获取URL后面参数
    C# List 某行数据置顶
    EF空字段使用contains查询的解决办法
    sql语句查询,多字段like模糊查询优化
    Asp.Net Core中间件
  • 原文地址:https://www.cnblogs.com/nangonghui/p/14845982.html
Copyright © 2020-2023  润新知