java缓存——(五)LocalCache本地缓存分享

LocalCache本地缓存分享

前言

一、本地缓存应用场景

二、java本地缓存标准

三、java开源缓存框架

四、LocalCache实现

结束语

 

前言

本次分享探讨java平台的本地缓存,是指占用JVM的heap区域来缓冲存储数据的缓存组件。

 

一、本地缓存应用场景


localcache有着极大的性能优势:

1. 单机情况下适当使用localcache会使应用的性能得到很大的提升。

2. 集群环境下对于敏感性要求不高的数据可以使用localcache,只配置简单的失效机制来保证数据的相对一致性。

哪些数据可以存储到本地缓存?

1.访问频繁的数据;

2.静态基础数据(长时间内不变的数据);

3.相对静态数据(短时间内不变的数据)。

 

二、java本地缓存标准

Java缓存新标准(javax.cache),这个标准由JSR107所提出,已经被包含在Java EE 7中。

特性:

1.原子操作,跟java.util.ConcurrentMap类似

2.从缓存中读取

3.写入缓存

4.缓存事件监听器

5.数据统计

6.包含所有隔离(ioslation)级别的事务

7.缓存注解(annotations)

8.保存定义key和值类型的泛型缓存

9.引用保存(只适用于堆缓存)和值保存定义

                  

但目前应用不是很普遍。

 

三、java开源缓存框架

比较有名的本地缓存开源框架有:

1.EHCache

EHCache是一个纯java的在进程中的缓存,它具有以下特性:快速,简单,为Hibernate2.1充当可插入的缓存,最小的依赖性,全面的文档和测试。

BUG: 过期失效的缓存元素无法被GC掉,时间越长缓存越多,内存占用越大,导致内存泄漏的概率越大。

 

2.OSCache

OSCache有以下特点:缓存任何对象,你可以不受限制的缓存部分jsp页面或HTTP请求,任何java对象都可以缓存。拥有全面的API--OSCache API给你全面的程序来控制所有的OSCache特性。永久缓存--缓存能随意的写入硬盘,因此允许昂贵的创建(expensive-to-create)数据来保持缓存,甚至能让应用重启。支持集群--集群缓存数据能被单个的进行参数配置,不需要修改代码。缓存记录的过期--你可以有最大限度的控制缓存对象的过期,包括可插入式的刷新策略(如果默认性能不需要时)。

 

3.JCache

Java缓存新标准(javax.cache)

 

4.cache4j

cache4j是一个有简单API与实现快速的Java对象缓存。它的特性包括:在内存中进行缓存,设计用于多线程环境,两种实现:同步与阻塞,多种缓存清除策略:LFU, LRU, FIFO,可使用强引用。

 

5.ShiftOne

ShiftOne Java Object Cache是一个执行一系列严格的对象缓存策略的Java lib,就像一个轻量级的配置缓存工作状态的框架。

 

6.WhirlyCache

Whirlycache是一个快速的、可配置的、存在于内存中的对象的缓存。

 

四、LocalCache实现

1、LocalCache简介

LocalCache是一个精简版本地缓存组件,有以下特点:

1.  有容量上限maxCapacity;

2.  缓存达到容量上限时基于LRU策略来移除缓存元素;

3.  缓存对象的生命周期(缓存失效时间)由调用方决定;

4.  缓存对象失效后,将会有定时清理线程来清理掉,不会导致内存泄漏。

5.  性能比Ehcache稍强。

 

2、总体设计

LocalCache总体设计:

1.  缓存元素 CacheElement;

2.  缓存容器 LRULinkedHashMap;

3.  缓存接口 Cache;

4.  缓存组件实现 LocalCache。

 

3、详细设计

1.  CacheElement设计

/**
 * 缓存元素
 *
 */
public class CacheElement {
    private Object key;
    private Object value;
    private long createTime;
    private long lifeTime;
    private int hitCount;
 
    public CacheElement() {
    }
 
    public CacheElement(Object key ,Object value) {
        this.key = key;
        this.value = value;
        this.createTime = System.currentTimeMillis();
    }
    
    public Object getKey() {
        return key;
    }
 
    public void setKey(Object key) {
        this.key = key;
    }
 
    public Object getValue() {
        hitCount++;
        return value;
    }
 
    public void setValue(Object value) {
        this.value = value;
    }
 
    public long getCreateTime() {
        return createTime;
    }
 
    public void setCreateTime(long createTime) {
        this.createTime = createTime;
    }
 
    public int getHitCount() {
        return hitCount;
    }
 
    public void setHitCount(int hitCount) {
        this.hitCount = hitCount;
    }
 
    public long getLifeTime() {
        return lifeTime;
    }
 
    public void setLifeTime(long lifeTime) {
        this.lifeTime = lifeTime;
    }
    
    public boolean isExpired() {
        boolean isExpired = System.currentTimeMillis() - getCreateTime() > getLifeTime();
        return isExpired;
    }
 
    /*
     * (non-Javadoc)
     * @see java.lang.Object#toString()
     */
    public String toString() {
        StringBuffer sb = new StringBuffer();
        sb.append("[ key=").append(key).append(", isExpired=").append(isExpired())
            .append(", lifeTime=").append(lifeTime).append(", createTime=").append(createTime)
            .append(", hitCount=").append(hitCount)
            .append(", value=").append(value).append(" ]");
        return sb.toString();
    }
    
    /*
     * (non-Javadoc)
     * @see java.lang.Object#hashCode()
     */
    public final int hashCode(){
        if(null == key){
            return "".hashCode();
        }
        return this.key.hashCode();
    }
    
    /*
     * (non-Javadoc)
     * @see java.lang.Object#equals(java.lang.Object)
     */
    public final boolean equals(Object object) {
        if ((object == null) || (!(object instanceof CacheElement))) {
            return false;
        }
 
        CacheElement element = (CacheElement) object;
        if ((this.key == null) || (element.getKey() == null)) {
            return false;
        }
 
        return this.key.equals(element.getKey());
    }
}

2.  LRULinkedHashMap实现

 

 
import java.util.LinkedHashMap;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
 * 实现 LRU策略的 LinkedHashMap
 *
 * @param 
 * @param 
 */
public class LRULinkedHashMap extends LinkedHashMap  
{  
    protected static final long serialVersionUID = 2828675280716975892L;
    
    protected static final int DEFAULT_MAX_ENTRIES = 100;
    
    protected final int initialCapacity; 
    protected final int maxCapacity; 
    protected boolean enableRemoveEldestEntry = true;//是否允许自动移除比较旧的元素(添加元素时)
    
    protected static final float DEFAULT_LOAD_FACTOR = 0.8f;  
    protected final Lock lock = new ReentrantLock();  
 
    public LRULinkedHashMap(int initialCapacity)  
    {  
        this(initialCapacity, DEFAULT_MAX_ENTRIES);
    }
    
    public LRULinkedHashMap(int initialCapacity ,int maxCapacity)  
    {  
        //set accessOrder=true, LRU
        super(initialCapacity, DEFAULT_LOAD_FACTOR, true); 
        
        this.initialCapacity = initialCapacity; 
        this.maxCapacity = maxCapacity; 
    }
 
    /*
     *   (non-Javadoc)
     * @see java.util.LinkedHashMap#removeEldestEntry(java.util.Map.Entry)
     */
    protected boolean removeEldestEntry(java.util.Map.Entry eldest)  
    {  
        return enableRemoveEldestEntry && ( size() > maxCapacity );
    }  
 
    /*
     *   (non-Javadoc)
     * @see java.util.LinkedHashMap#get(java.lang.Object)
     */
    public V get(Object key)  
    {  
        try {  
            lock.lock();  
            return super.get(key);  
        }  
        finally {  
            lock.unlock();  
        }  
    }  
 
    /*
     *   (non-Javadoc)
     * @see java.util.HashMap#put(java.lang.Object, java.lang.Object)
     */
    public V put(K key, V value)  
    {  
        try {  
            lock.lock();  
            return super.put(key, value);  
        }  
        finally {  
            lock.unlock();  
        }  
    }  
    
    /*
     * (non-Javadoc)
     * @see java.util.HashMap#remove(java.lang.Object)
     */
    public V remove(Object key) {
        try {  
            lock.lock();
            return super.remove(key);
        }  
        finally {  
            lock.unlock();  
        }
    }
    
    /*
     * (non-Javadoc)
     * @see java.util.LinkedHashMap#clear()
     */
    public void clear() {
         try {  
             lock.lock();
             super.clear();
         }  
         finally {  
             lock.unlock();
         }
    }
    
    /*
     * (non-Javadoc)
     * @see java.util.HashMap#keySet()
     */
    public Set keySet() {
        try {  
            lock.lock();
            return super.keySet();
        }  
        finally {  
            lock.unlock();
        }
    }
    
    public boolean isEnableRemoveEldestEntry() {
        return enableRemoveEldestEntry;
    }
    public void setEnableRemoveEldestEntry(boolean enableRemoveEldestEntry) {
        this.enableRemoveEldestEntry = enableRemoveEldestEntry;
    }
    public int getInitialCapacity() {
        return initialCapacity;
    }
    public int getMaxCapacity() {
        return maxCapacity;
    }
}  

3.  Cache接口设计

 

/**
 * 缓存接口
 *
 */
public interface Cache {
    
    /**
     * 获取缓存
     * @param key
     * @return
     */
    public  T getCache(Object key);
    
    /**
     * 缓存对象
     * @param key
     * @param value
     * @param milliSecond 缓存生命周期(毫秒)
     */
    public void putCache(Object key, Object value ,Long milliSecond);
    
    /**
     * 缓存容器中是否包含 key 
     * @param key
     * @return
     */
    public boolean containsKey(Object key);
    
    /**
     * 缓存列表大小
     * @return
     */
    public int getSize();
    
    /**
     * 是否启用缓存
     */
    public boolean isEnabled();
    /**
     * 启用 或 停止
     * @param enable
     */
    public void setEnabled(boolean enabled);
    
    /**
     * 移除所有缓存
     */
    public void invalidateCaches();
    
    /**
     * 移除 指定key缓存
     * @param key
     */
    public void invalidateCache(Object key);
}

4.  LocalCache实现

 

import java.util.Date;
import java.util.Iterator;
import java.util.Random;
 
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
 
/**
 * 本地缓存组件
 */
public class LocalCache implements Cache{
    private Logger logger = LoggerFactory.getLogger(this.getClass());
    
    private LRULinkedHashMap

结束语

开源或有BUG,入手需谨慎。 

原文:https://blog.youkuaiyun.com/u011683530/article/details/51029734

转载于:https://www.cnblogs.com/wynjauu/articles/9908302.html

### Java 中的缓存机制与解决方案 在 Java 开发中,缓存是一种常见的优化手段,用于减少重复计算或数据库查询的时间开销。以下是几种主流的 Java 缓存实现方式以及推荐使用的缓存库。 #### 1. **本地缓存** 通过简单的内存结构(如 `HashMap` 或 `ConcurrentHashMap`),可以在应用程序内部存储临时数据。这种方案适合小型应用或者单机环境下的性能提升需求[^1]。 ```java public class LocalCache { public static Map<String, String> cache = new ConcurrentHashMap<>(); static { String name = "1-" + UUID.randomUUID().toString(); cache.put("1", name); System.out.println("ID为1的数据已添加到缓存"); } } ``` 然而,在实际项目中并不建议单独依赖此类方法,因为它们缺乏过期策略、容量控制等功能[^2]。 --- #### 2. **Guava Cache** Google 提供的一个轻量级缓存工具 Guava Cache 是一种更专业的选择。它支持设置最大条目数、时间失效规则等特性: ```java import com.google.common.cache.CacheBuilder; import java.util.concurrent.TimeUnit; LoadingCache<String, String> guavaCache = CacheBuilder.newBuilder() .maximumSize(100) // 设置最多保存多少项 .expireAfterWrite(10, TimeUnit.MINUTES) // 数据写入后多久自动清除 .build(new CacheLoader<>() { @Override public String load(String key) throws Exception { return fetchValueFromDatabase(key); // 自定义加载逻辑 } }); ``` 此库适用于中小型规模的应用场景,并且易于集成和配置[^3]。 --- #### 3. **Caffeine Cache** 作为 Guava 的继任者之一,Caffeine 提供了更高的命中率算法 (W-TinyLFU),同时保持简洁易用的特点。其 API 设计类似于 Guava ,但性能表现更加优越: ```java import com.github.benmanes.caffeine.cache.Caffeine; import java.util.concurrent.TimeUnit; var caffeineCache = Caffeine.newBuilder() .initialCapacity(100) .maximumSize(500) .expireAfterAccess(10, TimeUnit.MINUTES) .recordStats() // 启用统计功能以便监控 .build((key) -> fetchDataByKey(key)); // 定义默认加载器 ``` 对于追求高性能和低延迟的服务来说,这是一个非常理想的选择[^4]。 --- #### 4. **Redis 缓存** 当需要跨多个实例共享状态时,则应考虑分布式缓存技术——比如 Redis 。它可以持久化部分重要信息并提供丰富的操作命令集满足复杂业务需求 : 安装好客户端驱动程序之后即可快速接入服务端接口 : ```xml <!-- Maven Dependency --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> ``` 接着利用 Spring Data Redis 组件简化交互流程: ```java @Autowired private StringRedisTemplate redisTemplate; void saveToRedis(String id, String value){ ValueOperations<String, String> ops = redisTemplate.opsForValue(); ops.set(id,value,"60","SECONDS"); // 存储键值对的同时指定有效期限 } Optional<String> getFromRedis(String id){ return Optional.ofNullable(redisTemplate.opsForValue().get(id)); } ``` 由于具备高可用性和扩展能力,因此特别适合作为企业级系统的首选方案[^5]。 --- #### 总结比较表 | 特性对比分析 | 方案名称 | 是否分布部署支持 | 过期处理 | 并发安全性 | |----------------|--------------------|--------------|-------------------| | 原生 HashMap | 不支持 | 手动维护 | 较差 | | Guava | 单节点内 | 支持多种模式 | 良好 | | Caffeine | 局限于单一 JVM 实例 | 动态调整策略 | 极佳 | | Redis | 全局范围兼容 | TTL 时间戳设定 | 生产环境下稳定可靠 | 每种类型的适用场合各有侧重,请依据具体应用场景选取最合适的选项。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值