1、缓存

将程序或系统中常用的数据对象存储在像内存这样特定的介质中,以避免在每次程序调用时,重新创建或组织数据所带来的性能损耗,从而提高了系统的整体运行速度

以目前的系统架构来说,用户的请求一般会先经过缓存系统,如果缓存中没有相关的数据,就会在其他系统中查询到相应的数据并保存在缓存中,最后返回给调用方

2、本地式缓存

本地缓存是指程序级别的缓存组件,它的特点是本地缓存和应用程序会运行在同一个进程中,所以本地缓存的操作会非常快,因为在同一个进程内也意味着不会有网络上的延迟和开销

本地缓存适用于单节点非集群的应用场景,它的优点是快,缺点是多程序无法共享缓存

无法共享缓存可能会造成系统资源的浪费,这是因为每个系统都单独维护了一份属于自己的缓存,而同一份缓存有可能被多个系统单独进行存储,从而浪费了系统资源

本地缓存可以使用 EhCache 和 Google 的 Guava 来实现

EhCache :

目前比较流行的开源缓存框架,是用纯 Java 语言实现的简单、快速的 Cache 组件

支持内存缓存和磁盘缓存

支持 LRU(Least Recently Used,最近很少使用)、LFU(Least Frequently Used,最近不常被使用)和 FIFO(First In First Out,先进先出)等多种淘汰算法

支持分布式的缓存系统

LRU 和 LFU 的区别:

LRU 算法有一个缺点,比如说很久没有使用的一个键值,如果最近被访问了一次,那么即使它是使用次数最少的缓存,它也不会被淘汰

LFU 算法解决了偶尔被访问一次之后,数据就不会被淘汰的问题,它是根据总访问次数来淘汰数据的,其核心思想是“如果数据过去被访问多次,那么将来它被访问次数也会比较多”

<!-- 引入依赖 -->
<dependency>
    <groupId>org.ehcache</groupId>
    <artifactId>ehcache</artifactId>
    <version>3.8.1</version>
</dependency>
// 创建缓存管理器,可以通过单例或者多例的方式创建,也是 Ehcache 的入口类
// 每个 CacheManager 可以管理多个 Cache,每个 Cache 可以采用 hash 的方式存储多个元素

CacheManager cacheManager= CacheManagerBuilder.newCacheManagerBuilder().build();
// 初始化 EhCache
cacheManager.init();
// 创建缓存(存储器)
Cache<String,String> myCache=cacheManager.createCache("MYCACHE",
CacheConfigurationBuilder.newCacheConfigurationBuilder(String.class,String.class,
ResourcePoolsBuilder.heap(10)));// 设置缓存的最大容量
// 设置缓存的最大容量
// 设置缓存
myCache.put("key","Hello");
// 读取缓存
String value = myCache.get("key");
// 输出缓存
System.out.println(value);
// 关闭缓存
cacheManager.close();

Guava :

Guava Cache 是 Google 开源的 Guava 里的一个子功能

是一个内存型的本地缓存实现方案,提供了线程安全的缓存操作机制

Guava Cache 的架构设计灵感来源于 ConcurrentHashMap,它使用了多个 segments 方式的细粒度锁,在保证线程安全的同时,支持了高并发的使用场景

Guava Cache 类似于 Map 集合的方式对键值对进行操作,只不过多了过期淘汰等处理逻辑

Guava Cache 的创建有两种方式,一种是 LoadingCache,另一种是 Callable

//添加依赖
<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>28.2-jre</version>
</dependency>
import com.google.common.cache.*;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit; public class GuavaExample {
    public static void main(String[] args) throws ExecutionException {
        // 创建方式一:LoadingCache
        LoadingCache<String, String> loadCache = CacheBuilder.newBuilder()
                // 并发级别设置为 5,是指可以同时写缓存的线程数
                .concurrencyLevel(5)
                // 设置 8 秒钟过期
                .expireAfterWrite(8, TimeUnit.SECONDS)
                //设置缓存容器的初始容量为 10
                .initialCapacity(10)
                // 设置缓存最大容量为 100,超过之后就会按照 LRU 算法移除缓存项
                .maximumSize(100)
                // 设置要统计缓存的命中率
                .recordStats()
                // 设置缓存的移除通知
                .removalListener(new RemovalListener<Object, Object>() {
                    public void onRemoval(RemovalNotification<Object, Object> notification) {
                        System.out.println(notification.getKey() + " was removed, cause is " + notification.getCause());
                    }
                })
                // 指定 CacheLoader,缓存不存在时,可自动加载缓存
                .build(
                        new CacheLoader<String, String>() {
                            @Override
                            public String load(String key) throws Exception {
                                // 自动加载缓存的业务
                                return "cache-value:" + key;
                            }
                        }
                );
        // 设置缓存
        loadCache.put("c1", "Hello, c1.");
        // 查询缓存
        String val = loadCache.get("c1");
        System.out.println(val);
        // 查询不存在的缓存
        String noval = loadCache.get("noval");
        System.out.println(noval);         // 创建方式二:Callable
        Cache<String, String> cache = CacheBuilder.newBuilder()
                .maximumSize(2) // 设置缓存最大长度
                .build();
        // 设置缓存
        cache.put("k1", "Hello, k1.");
        // 查询缓存
        String value = cache.get("k1", new Callable<String>() {
            @Override
            public String call() {
                // 缓存不存在时,执行
                return "nil";
            }
        });
        // 输出缓存值
        System.out.println(value);
        // 查询缓存
        String nokey = cache.get("nokey", new Callable<String>() {
            @Override
            public String call() {
                // 缓存不存在时,执行
                return "nil";
            }
        });
        // 输出缓存值
        System.out.println(nokey);
    }
}

3、分布式缓存

指将应用系统和缓存组件进行分离的缓存机制,这样多个应用系统就可以共享一套缓存数据了,它的特点是共享缓存服务和可集群部署,为缓存系统提供了高可用的运行环境,以及缓存共享的程序运行机制

分布式缓存可以使用 Redis 或 Memcached 来实现

4、手写一个缓存

(1)考虑存储数据的模型,并发情况下可以使用 Hashtable 或 ConcurrentHashMap,非并发的情况下可以使用HashMap

(2)缓存数据的有效时间、缓存淘汰

/**
* 缓存实体类,实现Comparable接口,重写compareTo方法
*/ public class CacheValue implements Comparable<CacheValue> {
//缓存键
private Object key;
//缓存值
private Object value;
//最后访问时间
private long lastTime;
//创建时间
private long writeTime;
//存活时间
private long expireTime;
//命中次数
private Integer hitCount; public Object getKey() {
return key;
} public void setKey(Object key) {
this.key = key;
} public Object getValue() {
return value;
} public void setValue(Object value) {
this.value = value;
} public long getLastTime() {
return lastTime;
} public void setLastTime(long lastTime) {
this.lastTime = lastTime;
} public long getWriteTime() {
return writeTime;
} public void setWriteTime(long writeTime) {
this.writeTime = writeTime;
} public long getExpireTime() {
return expireTime;
} public void setExpireTime(long expireTime) {
this.expireTime = expireTime;
} public Integer getHitCount() {
return hitCount;
} public void setHitCount(Integer hitCount) {
this.hitCount = hitCount;
} @Override
public int compareTo(CacheValue o) {
return hitCount.compareTo(o.hitCount);
}
}
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; /**
* Cache 全局类
*/
public class CacheGlobal {
// 全局缓存对象
public static ConcurrentMap<String, CacheValue> concurrentMap = new ConcurrentHashMap<>();
}

  

import java.util.concurrent.TimeUnit;

/*缓存操作类
*/
public class CacheUtils {
/**
*添加缓存
*@paramkey
*@paramvalue
*@paramexpire
*/
public void put(String key,Object value,long expire){
//判断key是否有值
if(key == null && !"".equals(key))
return ;
//当缓存存在时,更新缓存
if(CacheGlobal.concurrentMap.containsKey(key)){
CacheValue cache=CacheGlobal.concurrentMap.get(key);
cache.setHitCount(cache.getHitCount()+1);
cache.setWriteTime(System.currentTimeMillis());
cache.setLastTime(System.currentTimeMillis());
cache.setExpireTime(expire);
cache.setValue(value);
return;
}
//创建缓存
CacheValue cache=new CacheValue();
cache.setKey(key);
cache.setValue(value);
cache.setWriteTime(System.currentTimeMillis());
cache.setLastTime(System.currentTimeMillis());
cache.setHitCount(1);
cache.setExpireTime(expire);
CacheGlobal.concurrentMap.put(key,cache);
} /**
*获取缓存
*@paramkey
*@return
*/
public Object get(String key){
//判断key是否有值
if(key == null && !"".equals(key))
return null;
//字典中不存在
if(CacheGlobal.concurrentMap.isEmpty() && !CacheGlobal.concurrentMap.containsKey(key))
return null;
CacheValue cache=CacheGlobal.concurrentMap.get(key);
if(cache==null)
return null;
//惰性删除,判断缓存是否过期
long timoutTime= TimeUnit.NANOSECONDS.toSeconds(System.nanoTime()-cache.getWriteTime());
if(cache.getExpireTime()<=timoutTime)
//缓存过期
return null; //清除过期缓存
CacheGlobal.concurrentMap.remove(key);
cache.setHitCount(cache.getHitCount()+1);
cache.setLastTime(System.currentTimeMillis());
return cache.getValue();
}
}

  

import java.util.concurrent.TimeUnit;

/**
* 过期缓存检测
*/
public class ExpireThread implements Runnable{
@Override
public void run() {
while(true){
try {
//十秒钟线程检测一次
TimeUnit.SECONDS.sleep(10);
//调用检测方法
expireCache();
} catch (InterruptedException e) {
e.printStackTrace();
} } }
public void expireCache(){
System.out.println("开始检测缓存是否过期");
for(String key : CacheGlobal.concurrentMap.keySet()){
CacheValue cache = CacheGlobal.concurrentMap.get(key);
//缓存存在时间
long timeouttime = TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - cache.getWriteTime());
//判断缓存是否过期
if(cache.getExpireTime() > timeouttime){
//没过期,跳出循环
continue;
}
//删除过期缓存
CacheGlobal.concurrentMap.remove(key);
}
}
}

测试代码:

public class Test {
public static void main(String[] args){
CacheUtils cache=new CacheUtils();
//存入缓存
cache.put("key","老大",10);
//查询缓存
String val=(String)cache.get("key");
System.out.println(val);
//查询不存在的缓存
String noval=(String)cache.get("noval");
System.out.println(noval);
}
}

Java基础——缓存的更多相关文章

  1. Java基础知识强化10:Java中的中间缓存变量机制

    1.对于自增运算++j与j++,由于加一的执行顺序不同,所以Java中有中间缓存变量来储存其单个表达式的值,而j的自增自减的结果依然保留在原来的变量储存区.因为本体是j的值,而单个表达式的值是中间产生 ...

  2. Java基础知识(壹)

    写在前面的话 这篇博客,是很早之前自己的学习Java基础知识的,所记录的内容,仅仅是当时学习的一个总结随笔.现在分享出来,希望能帮助大家,如有不足的,希望大家支出. 后续会继续分享基础知识手记.希望能 ...

  3. [Java面经]干货整理, Java面试题(覆盖Java基础,Java高级,JavaEE,数据库,设计模式等)

    如若转载请注明出处: http://www.cnblogs.com/wang-meng/p/5898837.html   谢谢.上一篇发了一个找工作的面经, 找工作不宜, 希望这一篇的内容能够帮助到大 ...

  4. Java基础加强之多线程篇(线程创建与终止、互斥、通信、本地变量)

    线程创建与终止 线程创建 Thread类与Runnable接口的关系 public interface Runnable { public abstract void run(); } public ...

  5. Java基础进阶整理

    Java学习笔记整理 本文档是我个人整理的,首先是想通过完成本文档更加扎实自己的基础加强对java语言的理解,然后就是想给入了门的同志们做下贡献. 当然,本文档主要是对java语言基础(当然还有很多基 ...

  6. Java基础常见英语词汇

    Java基础常见英语词汇(共70个) ['ɔbdʒekt] ['ɔ:rientid]导向的                             ['prəʊɡræmɪŋ]编程 OO: object ...

  7. 经典的Java基础面试题集锦

    经典的Java基础面试题集锦,欢迎收藏和分享. 问题:如果main方法被声明为private会怎样? 答案:能正常编译,但运行的时候会提示”main方法不是public的”. 问题:Java里的传引用 ...

  8. java基础3.0:Java常用API

    本篇介绍Java基础中常用API使用,当然只是简单介绍,围绕重要知识点引入,巩固开发知识,深入了解每个API的使用,查看JavaAPI文档是必不可少的. 一.java.lang包下的API Java常 ...

  9. JAVA基础知识之网络编程——-网络基础(Java的http get和post请求,多线程下载)

    本文主要介绍java.net下为网络编程提供的一些基础包,InetAddress代表一个IP协议对象,可以用来获取IP地址,Host name之类的信息.URL和URLConnect可以用来访问web ...

随机推荐

  1. 重拾Java Web应用的基础体系结构

    目录 一.背景 二.Web应用 2.1 HTML 2.2 HTTP 2.3 URL 2.4 Servlet 2.4.1 编写第一个Servlet程序 2.5 JSP 2.6 容器 2.7 URL映射到 ...

  2. css基本样式设置

    div中文字居中 如何让一个div中的文字水平和垂直居中?设置如下: 给定该div的长宽(或者二者只给出其一也可) .box{ height: 100px; width: 30%; text-alig ...

  3. Java面试题 200+

    面试题包含的内容了十九了模块:Java 基础.容器.多线程.反射.对象拷贝.Java Web 模块.异常.网络.设计模式.Spring/Spring MVC.Spring Boot/Spring Cl ...

  4. CSP 202006-1 线性分类器python实现

    思路 这题问题是对于这一群点和一条直线,我们也不知道直线上方的是A类还是直线下方的是A类.其实对于这个二分类问题,我们也没必要知道.我们只需要判断直线每一测的点是不是一类(A类或B类)就可以了. 至于 ...

  5. python os库的使用方法 + 自动化安装第三方库脚本

    一.os库基本介绍 os库提供通用的.基本的操作系统交互功能,包括windows.Mac os.linux os库是python标准库,包含几百个函数 常用路径操作.进程管理.环境参数等几类 路径操作 ...

  6. Selenium多浏览器处理

    当我们在执行自动化测试过程中,往往会针对不同的浏览器做兼容性测试,那么我们在代码中,可以针对执行命令传过来的参数,选择对应的浏览器来执行测试用例 代码如下: 在终端中执行命令如上图红框中所示: bro ...

  7. 长沙做假证u

    长沙做假证[电/薇:187ヘ1184ヘ0909同号]办各类证件-办毕业证-办离婚证,办学位证书,办硕士毕业证,办理文凭学历,办资格证,办房产证不. 这是一个简单的取最大值程序,可以用于处理 i32 数 ...

  8. JavaScript 究竟是怎样去执行的?

    摘要: 理解 JS 引擎运行原理. 作者:前端小智 原文:搞懂 JavaScript 引擎运行原理 Fundebug经授权转载,版权归原作者所有. 一些名词 JS 引擎 — 一个读取代码并运行的引擎, ...

  9. C# OWC11

    public void OcwChart(int[] Data,string[] DataName,string Yname,string Xname,string ChartName,string ...

  10. 深入了解Kafka【一】概述与基础架构

    1.概述 Kafka是一个分布式的.基于发布订阅的消息系统,主要解决应用解耦.异步消息.流量削峰等问题. 2.发布订阅模型 消息生产者将消息发布到Topic中,同时有多个消息消费者订阅该消息,消费者消 ...