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));
    }
}
原文地址:https://www.cnblogs.com/nangonghui/p/14845982.html