当前位置: 首页 > article >正文

guava本地缓存+自定义线程工厂和线程池

文章目录

  • guava本地缓存
    • 1.引入依赖
        • 1.sun-dependencies
        • 2.sun-common-redis
    • 2.CacheUtil.java封装和使用
        • 1.CacheUtil.java
        • 2.使用方式
  • 自定义线程工厂+自定义线程池
    • 1.自定义线程工厂
        • 1.新建一个线程池模块 sun-common-threadpool
        • 2.CustomNameThreadFactory.java 自定义线程工厂
    • 2.sun-demo模块自定义线程池
        • 1.ThreadPoolConfig.java
        • 2.引入sun-common-threadpool的依赖
        • 3.测试

guava本地缓存

1.引入依赖

1.sun-dependencies
            <guava.version>19.0</guava.version>
           	<fastjson.version>1.2.83</fastjson.version>

            <!-- guava本地缓存 -->
            <dependency>
                <groupId>com.google.guava</groupId>
                <artifactId>guava</artifactId>
                <version>${guava.version}</version>
            </dependency>
            <!-- fastjson序列化 -->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>${fastjson.version}</version>
            </dependency>
2.sun-common-redis
        <!-- guava本地缓存 -->
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
        </dependency>
        <!-- fastjson序列化 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
        </dependency>

2.CacheUtil.java封装和使用

1.CacheUtil.java
package com.sunxiansheng.redis.util;

import com.alibaba.fastjson.JSON;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

/**
 * Description: Guava本地缓存工具类
 * @Author sun
 * @Create 2024/7/22 12:55
 * @Version 1.0
 */
@Component
@Slf4j
public class CacheUtil<K, V> {

    // 读取配置文件,支持动态刷新(需要配置Nacos)
    @Value("${guaca.cache.switch:false}")
    private Boolean cacheEnabled;

    @Value("${guaca.cache.maximumSize:5000}")
    private int maximumSize;

    @Value("${guaca.cache.expireAfterAccess:3}")
    private int expireAfterAccess;

    /**
     * 缓存key连接符
     */
    private static final String CACHE_KEY_SEPARATOR = "_";

    // 初始化一个Guava缓存
    private final Cache<String, String> localCache = CacheBuilder.newBuilder()
            .maximumSize(maximumSize)
            .expireAfterAccess(expireAfterAccess, TimeUnit.SECONDS)
            .build();

    /**
     * 使用本地缓存获取结果(批量)
     *
     * @param skuIdList id列表
     * @param cachePrefix 查询的前缀(缓存的key由前缀 + "_" + id构成)
     * @param clazz 查询完结果后要反序列化的类型
     * @param function 不走缓存查询结果的逻辑
     * @return 查询结果的Map
     */
    public Map<K, V> getResult(List<K> skuIdList, String cachePrefix, Class<V> clazz, Function<List<K>, Map<K, V>> function) {
        // 判空
        if (CollectionUtils.isEmpty(skuIdList)) {
            return Collections.emptyMap();
        }

        Map<K, V> resultMap = new HashMap<>(16);

        // 如果缓存开关未开启,直接使用function查询
        if (Boolean.FALSE.equals(cacheEnabled)) {
            return function.apply(skuIdList);
        }

        // 记录未命中缓存的ID
        List<K> noCacheIdList = new LinkedList<>();

        // 查Guava本地缓存
        for (K skuId : skuIdList) {
            String cacheKey = generateCacheKey(cachePrefix, skuId);
            String content = localCache.getIfPresent(cacheKey);
            if (StringUtils.isNotBlank(content)) {
                V v = JSON.parseObject(content, clazz);
                resultMap.put(skuId, v);
            } else {
                noCacheIdList.add(skuId);
            }
        }

        // 记录未命中缓存的日志
        if (!CollectionUtils.isEmpty(noCacheIdList)) {
            log.debug("Cache miss for keys: {}", noCacheIdList);
        }

        // 如果所有ID都命中缓存,直接返回结果
        if (CollectionUtils.isEmpty(noCacheIdList)) {
            return resultMap;
        }

        // 使用function查询未命中缓存的ID
        Map<K, V> noCacheResultMap = function.apply(noCacheIdList);
        if (!CollectionUtils.isEmpty(noCacheResultMap)) {
            for (Map.Entry<K, V> entry : noCacheResultMap.entrySet()) {
                K skuId = entry.getKey();
                V content = entry.getValue();
                resultMap.put(skuId, content);
                String cacheKey = generateCacheKey(cachePrefix, skuId);
                localCache.put(cacheKey, JSON.toJSONString(content));
            }
        }

        return resultMap;
    }

    /**
     * 使用本地缓存获取单个结果
     *
     * @param key 缓存的键
     * @param cachePrefix 查询的前缀(缓存的key由前缀 + "_" + key构成)
     * @param clazz 查询完结果后要反序列化的类型
     * @param function 不走缓存查询结果的逻辑
     * @return 查询结果
     * @throws ExecutionException
     */
    public V getSingleResult(K key, String cachePrefix, Class<V> clazz, Function<K, V> function) throws ExecutionException {
        String cacheKey = generateCacheKey(cachePrefix, key);
        String content = localCache.getIfPresent(cacheKey);
        if (StringUtils.isNotBlank(content)) {
            return JSON.parseObject(content, clazz);
        }

        V result = function.apply(key);
        if (result != null) {
            localCache.put(cacheKey, JSON.toJSONString(result));
        }

        return result;
    }

    /**
     * 清除单个缓存
     *
     * @param key 缓存的键
     * @param cachePrefix 查询的前缀(缓存的key由前缀 + "_" + key构成)
     */
    public void invalidateSingleCache(K key, String cachePrefix) {
        String cacheKey = generateCacheKey(cachePrefix, key);
        localCache.invalidate(cacheKey);
        log.debug("Cache invalidated for key: {}", cacheKey);
    }

    /**
     * 清除多个缓存
     *
     * @param keys 缓存的键列表
     * @param cachePrefix 查询的前缀(缓存的key由前缀 + "_" + key构成)
     */
    public void invalidateMultipleCaches(List<K> keys, String cachePrefix) {
        List<String> cacheKeys = new ArrayList<>();
        for (K key : keys) {
            String cacheKey = generateCacheKey(cachePrefix, key);
            cacheKeys.add(cacheKey);
            log.debug("Cache invalidated for key: {}", cacheKey);
        }
        localCache.invalidateAll(cacheKeys);
    }

    /**
     * 清除所有缓存
     */
    public void invalidateAllCaches() {
        localCache.invalidateAll();
        log.debug("All caches invalidated.");
    }

    /**
     * 生成缓存键
     *
     * @param prefix 缓存前缀
     * @param id 缓存ID
     * @return 缓存键
     */
    private String generateCacheKey(String prefix, K id) {
        return prefix + CACHE_KEY_SEPARATOR + id;
    }
}
2.使用方式
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;

@Service
public class CacheService {

    @Autowired
    private CacheUtil<String, String> cacheUtil;

    /**
     * 查询批量数据
     * @param ids 数据ID列表
     * @return 查询结果的Map
     */
    public Map<String, String> queryData(List<String> ids) {
        // 调用cacheUtil获取数据
        return cacheUtil.getResult(ids, "dataPrefix", String.class, this::loadFromDatabase);
    }

    /**
     * 查询单个数据
     * @param id 数据ID
     * @return 查询结果
     * @throws ExecutionException
     */
    public String querySingleData(String id) throws ExecutionException {
        // 调用cacheUtil获取单个数据
        return cacheUtil.getSingleResult(id, "dataPrefix", String.class, this::loadSingleFromDatabase);
    }

    /**
     * 更新数据并清除相关缓存
     * @param id 数据ID
     * @param newValue 新的数据值
     */
    public void updateData(String id, String newValue) {
        // 更新数据到数据库
        saveToDatabase(id, newValue);
        // 清除单个缓存
        cacheUtil.invalidateSingleCache(id, "dataPrefix");
    }

    /**
     * 批量更新数据并清除相关缓存
     * @param dataMap 数据Map
     */
    public void updateMultipleData(Map<String, String> dataMap) {
        // 更新数据到数据库
        saveMultipleToDatabase(dataMap);
        // 清除多个缓存
        cacheUtil.invalidateMultipleCaches(new ArrayList<>(dataMap.keySet()), "dataPrefix");
    }

    /**
     * 清除所有缓存
     */
    public void clearAllCaches() {
        cacheUtil.invalidateAllCaches();
    }

    // 模拟从数据库加载批量数据
    private Map<String, String> loadFromDatabase(List<String> ids) {
        // 实际实现时应从数据库或其他数据源加载数据
        // 这里简单模拟返回
        return Map.of("id1", "Value for id1", "id2", "Value for id2", "id3", "Value for id3");
    }

    // 模拟从数据库加载单个数据
    private String loadSingleFromDatabase(String id) {
        // 实际实现时应从数据库或其他数据源加载数据
        // 这里简单模拟返回
        return "Value for " + id;
    }

    // 模拟保存单个数据到数据库
    private void saveToDatabase(String id, String value) {
        // 实际实现时应保存到数据库或其他数据源
        // 这里简单模拟
        System.out.println("Saved " + value + " for " + id + " to database");
    }

    // 模拟保存多个数据到数据库
    private void saveMultipleToDatabase(Map<String, String> dataMap) {
        // 实际实现时应保存到数据库或其他数据源
        // 这里简单模拟
        dataMap.forEach((id, value) -> System.out.println("Saved " + value + " for " + id + " to database"));
    }
}

自定义线程工厂+自定义线程池

1.自定义线程工厂

1.新建一个线程池模块 sun-common-threadpool
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.sunxiansheng</groupId>
        <artifactId>sun-common</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>sun-common-threadpool</artifactId>
    <!-- 子模块的version,如果不写就默认跟父模块的一样 -->
    <version>${children.version}</version>

</project>
2.CustomNameThreadFactory.java 自定义线程工厂
package com.sunxiansheng.threadpool.config;

import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Description: 自定义的线程工厂
 * @Author sun
 * @Create 2024/7/24 16:32
 * @Version 1.0
 */
public class CustomNameThreadFactory implements ThreadFactory {
    /**
     * 线程池计数器(当创建一个新的线程工厂实例时,该计数器递增,区分不同的线程池)
     */
    private static final AtomicInteger poolNumber = new AtomicInteger(1);
    /**
     * 线程计数器(用于记录由该线程工厂创建的线程数量,每创建一个新线程,该计数器递增)
     */
    private final AtomicInteger threadNumber = new AtomicInteger(1);
    /**
     * 线程组(线程所属的线程组,通常用于管理线程的集合)
     */
    private final ThreadGroup threadGroup;
    /**
     * 线程名字前缀(用于标识线程池和线程编号的前缀)
     */
    private final String namePrefix;

    /**
     * 构造函数:每次创建一个新的线程工厂实例时,确定该工厂所生成线程的名字前缀
     *
     * @param threadName 自定义线程名称前缀,相当于线程池的名称
     */
    public CustomNameThreadFactory(String threadName) {
        // 获取当前的安全管理器,以确定线程组
        SecurityManager securityManager = System.getSecurityManager();
        threadGroup =
                (securityManager != null)
                        ? securityManager.getThreadGroup()
                        : Thread.currentThread().getThreadGroup();

        // 如果没有设置线程名称,赋予默认值"pool"
        if (threadName == null || "".equals(threadName.trim())) {
            threadName = "pool";
        }

        // 生成线程名字前缀,格式为:线程名-线程池号-thread-
        namePrefix = threadName
                + "-" + poolNumber.getAndIncrement()
                + "-thread-";
    }

    /**
     * 创建一个新的线程,并应用自定义的设置
     *
     * @param r 要在新线程中执行的 Runnable 对象
     * @return 新创建的线程对象
     */
    @Override
    public Thread newThread(Runnable r) {
        // 创建线程,指定名字格式为:线程名-线程池号-thread-线程号
        Thread thread = new Thread(threadGroup, r, namePrefix + threadNumber.getAndIncrement());

        // ============================== 自定义的对创建的线程操作的逻辑 ==============================
        // 检查是否为守护线程
        if (thread.isDaemon()) {
            // 如果是守护线程,则设置为非守护线程
            thread.setDaemon(false);
        }
        // 判断线程的优先级是否为默认优先级5
        if (thread.getPriority() != Thread.NORM_PRIORITY) {
            // 如果不是,则将该线程设置为默认优先级
            thread.setPriority(Thread.NORM_PRIORITY);
        }
        // ============================== 自定义的对创建的线程操作的逻辑 ==============================

        return thread;
    }
}

2.sun-demo模块自定义线程池

1.ThreadPoolConfig.java
package com.sunxiansheng.user.config;

import com.sunxiansheng.threadpool.config.CustomNameThreadFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * Description: 自定义线程池
 * @Author sun
 * @Create 2024/7/24 16:57
 * @Version 1.0
 */
@Configuration
public class ThreadPoolConfig {

    /**
     * 创建并配置一个自定义的线程池
     *
     * @return 配置好的 ThreadPoolExecutor 实例
     */
    @Bean(name = "myThreadPool")
    public ThreadPoolExecutor myThreadPool() {
        // 创建一个自定义的线程工厂,指定线程池的名字
        CustomNameThreadFactory customNameThreadFactory = new CustomNameThreadFactory("myThreadPool");

        // 创建线程池,并设置其核心参数
        return new ThreadPoolExecutor(
                10, // 核心线程数
                10, // 最大线程数
                5, // 线程空闲时间
                TimeUnit.SECONDS, // 线程空闲时间的单位
                new LinkedBlockingDeque<>(), // 任务队列
                customNameThreadFactory, // 自定义线程工厂
                new ThreadPoolExecutor.CallerRunsPolicy() // 饱和策略
        );
    }
}
2.引入sun-common-threadpool的依赖
        <!-- 引入sun-common-threadpool -->
        <dependency>
            <groupId>com.sunxiansheng</groupId>
            <artifactId>sun-common-threadpool</artifactId>
            <version>${sun-common-version}</version>
        </dependency>
3.测试
package com.sunxiansheng.user.config;

import com.sunxiansheng.user.UserApplicaion;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import javax.annotation.Resource;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * Description:
 * @Author sun
 * @Create 2024/7/24 17:04
 * @Version 1.0
 */
// 指定项目启动类,并指定随机端口启动,防止与其他的东西冲突
@SpringBootTest(classes = UserApplicaion.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
// 规定配置
@RunWith(SpringRunner.class)
@Slf4j
class ThreadPoolConfigTest {

    /**
     * 依赖注入自定义的线程池
     */
    @Resource(name = "myThreadPool")
    private ThreadPoolExecutor threadPoolExecutor;

    @Test
    void myThreadPool() {
        for (int i = 0; i < 10; i++) {
            // 提交任务给线程池
            threadPoolExecutor.submit(() -> {
                log.info("当前时间:{}", System.currentTimeMillis());
            });
        }
    }
}

http://www.kler.cn/a/448944.html

相关文章:

  • 1 软件工程——概述
  • “AI 线索精益模型调用系统:开启精准营销新引擎
  • 记录仪方案_记录仪安卓主板定制_音视频记录仪PCBA定制开发
  • MySQL表名传参SP
  • 数字经济下的 AR 眼镜
  • 页面无滚动条,里面div各自有滚动条
  • Day 15:Spring 框架基础
  • Sass变量的妙用:提升CSS开发效率与可维护性
  • Web安全攻防入门教程——hvv行动详解
  • 深入理解 OpenCV 的距离变换(cv2.distanceTransform)及其应用
  • 生鲜电商新篇章:在线销售系统的创新设计
  • 二叉树总结
  • 【IMU:视觉惯性SLAM系统】
  • Redis分布式锁释放锁是否必须用lua脚本?
  • 聊一聊性能测试是如何开展的?
  • Unittest框架及自动化测试实现流程
  • Blender 中投影仪的配置与使用
  • RT-DETR融合[ECCV2024]FADformer中的FFCM模块
  • 【1 day】OtterRoot:Netfilter 通用 Root
  • 第22天:信息收集-Web应用各语言框架安全组件联动系统数据特征人工分析识别项目
  • SecureCRT汉化版
  • 3D架构图软件 iCraft Editor 正式发布 @icraftplayer-react 前端组件, 轻松嵌入3D架构图到您的项目,实现数字孪生
  • 关于JavaScript中的this-笔记
  • AAAI-2024 | 大语言模型赋能导航决策!NavGPT:基于大模型显式推理的视觉语言导航
  • 项目底链华为链切换长安链经验总结
  • 简易CPU设计入门:本系统中的通用寄存器(一)