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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM