Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >输了!广州某小厂一面,也凉了

输了!广州某小厂一面,也凉了

作者头像
小林coding
发布于 2024-05-28 04:29:09
发布于 2024-05-28 04:29:09
24900
代码可运行
举报
文章被收录于专栏:小林coding小林coding
运行总次数:0
代码可运行

图解学习网站:https://xiaolincoding.com

大家好,我是小林。

之前「后端面经」系列已经分享过北京、深圳、上海等地方的小厂面经,有同学想看看广州的。

那么今天来分享一个广州小厂的Java后端面经,面试问了 40 分钟,除了拷打项目的问题,还问了一些技术面试题。

不算难,可惜同学一些问题没有回答好,最后还是挂了,其实面试官人很好,整个交流也比较舒服,是自己太菜了。

考察的范围:Java 基础、Java 集合、Java 并发、Spring、Linux 这些知识点。无算法,MySQLRedis 、网络、系统也都没有问。

面试八股

Arraylist和LinkedList区别

  • 底层数据结构:ArrayList使用数组作为底层数据结构,而LinkedList使用双向链表作为底层数据结构
  • 随机访问性能:ArrayList支持通过索引直接访问元素,因为底层数组的连续存储特性,所以时间复杂度为O(1)。而LinkedList需要从头或尾部开始遍历链表,时间复杂度为O(n)。
  • 插入和删除操作:ArrayList在尾部插入和删除元素的时间复杂度为O(1),因为它只需要调整数组的长度即可。但在中间或头部插入和删除元素时,需要将后续元素进行移动,时间复杂度为O(n)。而LinkedList在任意位置插入和删除元素的时间复杂度为O(1),因为只需要调整节点的指针即可。
  • 内存占用:ArrayList在每个元素中都存储了实际的数据,而LinkedList在每个节点中存储了数据和前后节点的指针。因此,相同数量的元素情况下,LinkedList通常比ArrayList占用更多的内存空间。

ArrayList的扩容机制说一下

ArrayList在添加元素时,如果当前元素个数已经达到了内部数组的容量上限,就会触发扩容操作。ArrayList的扩容操作主要包括以下几个步骤:

  • 计算新的容量:一般情况下,新的容量会扩大为原容量的1.5倍(在JDK 10之后,扩容策略做了调整),然后检查是否超过了最大容量限制。
  • 创建新的数组:根据计算得到的新容量,创建一个新的更大的数组。
  • 将元素复制:将原来数组中的元素逐个复制到新数组中。
  • 更新引用:将ArrayList内部指向原数组的引用指向新数组。
  • 完成扩容:扩容完成后,可以继续添加新元素。

ArrayList的扩容操作涉及到数组的复制和内存的重新分配,所以在频繁添加大量元素时,扩容操作可能会影响性能。为了减少扩容带来的性能损耗,可以在初始化ArrayList时预分配足够大的容量,避免频繁触发扩容操作。

之所以扩容是 1.5 倍,是因为 1.5 可以充分利用移位操作,减少浮点数或者运算时间和运算次数。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 新容量计算
int newCapacity = oldCapacity + (oldCapacity >> 1);

线程安全的 List, CopyonWriteArraylist是如何实现线程安全的

CopyOnWriteArrayList底层也是通过一个数组保存数据,使用volatile关键字修饰数组,保证当前线程对数组对象重新赋值后,其他线程可以及时感知到。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
private transient volatile Object[] array;

在写入操作时,加了一把互斥锁ReentrantLock以保证线程安全。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public boolean add(E e) {
    //获取锁
    final ReentrantLock lock = this.lock;
    //加锁
    lock.lock();
    try {
        //获取到当前List集合保存数据的数组
        Object[] elements = getArray();
        //获取该数组的长度(这是一个伏笔,同时len也是新数组的最后一个元素的索引值)
        int len = elements.length;
        //将当前数组拷贝一份的同时,让其长度加1
        Object[] newElements = Arrays.copyOf(elements, len + 1);
        //将加入的元素放在新数组最后一位,len不是旧数组长度吗,为什么现在用它当成新数组的最后一个元素的下标?建议自行画图推演,就很容易理解。
        newElements[len] = e;
        //替换引用,将数组的引用指向给新数组的地址
        setArray(newElements);
        return true;
    } finally {
        //释放锁
        lock.unlock();
    }
}

看到源码可以知道写入新元素时,首先会先将原来的数组拷贝一份并且让原来数组的长度+1后就得到了一个新数组,新数组里的元素和旧数组的元素一样并且长度比旧数组多一个长度,然后将新加入的元素放置都在新数组最后一个位置后,用新数组的地址替换掉老数组的地址就能得到最新的数据了。

在我们执行替换地址操作之前,读取的是老数组的数据,数据是有效数据;执行替换地址操作之后,读取的是新数组的数据,同样也是有效数据,而且使用该方式能比读写都加锁要更加的效率。

现在我们来看读操作,读是没有加锁的,所以读是一直都能读

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public E get(int index) {
    return get(getArray(), index);
}

Stream流的并行API是什么?

是 ParallelStream。

并行流(ParallelStream)就是将源数据分为多个子流对象进行多线程操作,然后将处理的结果再汇总为一个流对象,底层是使用通用的 fork/join 池来实现,即将一个任务拆分成多个“小任务”并行计算,再把多个“小任务”的结果合并成总的计算结果

Stream串行流与并行流的主要区别:

对CPU密集型的任务来说,并行流使用ForkJoinPool线程池,为每个CPU分配一个任务,这是非常有效率的,但是如果任务不是CPU密集的,而是I/O密集的,并且任务数相对线程数比较大,那么直接用ParallelStream并不是很好的选择。

底层使用线程池的核心线程数

ParallelStream 底层使用了 ForkJoinPool 线程池。

ForkJoinPool 与 ThreadPoolExecutor 是完全不同的实现机制。ForkJoinPool 有四个参数可以设置:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public ForkJoinPool(int parallelism,
                        ForkJoinWorkerThreadFactory factory,
                        UncaughtExceptionHandler handler,
                        boolean asyncMode) {
        this(checkParallelism(parallelism),
             checkFactory(factory),
             handler,
             asyncMode ? FIFO_QUEUE : LIFO_QUEUE,
             "ForkJoinPool-" + nextPoolId() + "-worker-");
        checkPermission();
    }

但其实关键参数也只有一个线程数 parallelism(并行度,默认为CPU数,最小为1),其它参数影响不大。ForkJoinPool 机制特殊,每个线程都有自己独立的队列,并且没有核心线程和最大线程的概念。ForkJoinPool 线程数的设置很简单,分 cpu 密集型任务和 io 密集型任务两种情况考虑。对于 cpu 密集型任务,建议都用静态的 commonPool(线程数为 CPU 核心数 - 1,JDK 已经设置好了)原因是,如果 new 出多各 ForkJoinPool:

  • 浪费内存:ForkJoinPool 内存占用与线程数成正比,而且还挺大,可以计算,一个队列一个 ForkJoinTask 数组,数组初始容量就有 2^13,存储的是 ForkJoinTask 引用,一个对象引用占用 4 字节,那么一个队列占用 2^13 * 4 + 8 Byte 约等于 32 KB,如果设置了 16 线程,那么会有 32 个队列(大于等于线程数的最小 2 的 n 次幂乘 2),也就是 1M,多一个 16 线程的 ForkJoinPool 仅队列自身,一个任务都没有就多占用 1M 内存,再加上拆分出一堆子任务占用内存更多;
  • 影响性能:多占用内存,不仅没有性能提升,反而可能有性能损耗,cpu 密集型任务线程数最多也就 cpu 核心数,再多除了增加线程上下文切换次数没什么意义。

对于 io 密集型任务,线程数设置逻辑与 ThreadPoolExecutor 类似,队列容量无限大不可设置,在此基础上寻找合适的线程数即可。

CPU密集型和IO密集型 N+ 1 2N 具体是如何计算的?

N代表的是 CPU核数

  • CPU密集型:corePoolSize = CPU核数 + 1
  • IO密集型:corePoolSize = CPU核数 * 2

在《Java并发编程实践》中,是这样来计算线程池的线程数目的:

这种计算方式,我们需要知道上面定义的几个数值,才能计算出来线程池需要设置的线程数。其中,CPU数量是确定的,CPU使用率是目标值也是确定的,W/C也是可以通过基准程序测试得出的。

对于计算密集型应用,假定等待时间趋近于0,是的CPU利用率达到100%,那么线程数就是CPU核心数,那这个+1意义何在呢?《Java并发编程实践》这么说:

计算密集型的线程恰好在某时因为发生一个页错误或者因其他原因而暂停,刚好有一个“额外”的线程,可以确保在这种情况下CPU周期不会中断工作。

所以 N+1 是一个经验值。

那么对于对于IO密集型应用,假定所有的操作时间几乎都是IO操作耗时,那么W/C的值就为1,那么对应的线程数确实为2N。

说一下你对 spring的IOC 的理解

IOC:Inversion Of Control,即控制反转,是一种设计思想。在传统的 Java SE 程序设计中,我们直接在对象内部通过 new 的方式来创建对象,是程序主动创建依赖对象;

而在Spring程序设计中,IOC 是有专门的容器去控制对象。

所谓控制就是对象的创建、初始化、销毁。

  • 创建对象:原来是 new 一个,现在是由 Spring 容器创建。
  • 初始化对象:原来是对象自己通过构造器或者 setter 方法给依赖的对象赋值,现在是由 Spring 容器自动注入。
  • 销毁对象:原来是直接给对象赋值 null 或做一些销毁操作,现在是 Spring 容器管理生命周期负责销毁对象。

总结:IOC 解决了繁琐的对象生命周期的操作,解耦了我们的代码。所谓反转:其实是反转的控制权,前面提到是由 Spring 来控制对象的生命周期,那么对象的控制就完全脱离了我们的控制,控制权交给了 Spring 。这个反转是指:我们由对象的控制者变成了 IOC 的被动控制者。

spring 三级缓存是什么?

三级缓存主要是为了解决单例模式下的循环依赖的问题。

循环依赖指的是两个类中的属性相互依赖对方:例如 A 类中有 B 属性,B 类中有 A属性,从而形成了一个依赖闭环,如下图。

循环依赖问题在Spring中主要有三种情况:

  • 第一种:通过构造方法进行依赖注入时产生的循环依赖问题。
  • 第二种:通过setter方法进行依赖注入且是在多例(原型)模式下产生的循环依赖问题。
  • 第三种:通过setter方法进行依赖注入且是在单例模式下产生的循环依赖问题。

只有【第三种方式】的循环依赖问题被 Spring 解决了,其他两种方式在遇到循环依赖问题时,Spring都会产生异常。

Spring 解决单例模式下的setter循环依赖问题的主要方式是通过三级缓存解决循环依赖。三级缓存指的是 Spring 在创建 Bean 的过程中,通过三级缓存(缓存的底层都是Map)来缓存正在创建的 Bean,以及已经创建完成的 Bean 实例。具体步骤如下:

  • 实例化 Bean:Spring 在实例化 Bean 时,会先创建一个空的 Bean 对象,并将其放入一级缓存中。
  • 属性赋值:Spring 开始对 Bean 进行属性赋值,如果发现循环依赖,会将当前 Bean 对象提前暴露给后续需要依赖的 Bean(通过提前暴露的方式解决循环依赖)。
  • 初始化 Bean:完成属性赋值后,Spring 将 Bean 进行初始化,并将其放入二级缓存中。
  • 注入依赖:Spring 继续对 Bean 进行依赖注入,如果发现循环依赖,会从二级缓存中获取已经完成初始化的 Bean 实例。

通过三级缓存的机制,Spring 能够在处理循环依赖时,确保及时暴露正在创建的 Bean 对象,并能够正确地注入已经初始化的 Bean 实例,从而解决循环依赖问题,保证应用程序的正常运行。

三级缓存都是CurrentHashMap吗?

不是,只有一级用了ConcurrentHashMap,二级和三级用的是HashMap。

Spring利用三级Bean缓存的方式解决Bean循环依赖的问题,三级缓存也就是DefaultSingletonBeanRegistry中的三个Map:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/** Cache of singleton objects: bean name to bean instance. 一级缓存*/
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
 
/** Cache of early singleton objects: bean name to bean instance. 二级缓存*/
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
 
/** Cache of singleton factories: bean name to ObjectFactory. 三级缓存*/
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
  • singletonObjects:一级缓存,存放完整的Bean;
  • earlySingletonObjects:二级缓存,存放早期Bean,即还没有设置属性的Bean;
  • singletonFactories:三级缓存,存放ObjectFactory对象,即Bean的生产工厂。

但是在 spring 2.7.10新版本开始,earlySingletonObjects(二级缓存)改成使用 ConcurrentHashMap。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/** Cache of singleton objects: bean name to bean instance. 一级缓存 */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

/** Cache of early singleton objects: bean name to bean instance. 二级缓存 */
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);

/** Cache of singleton factories: bean name to ObjectFactory. 三级缓存 */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

Linux的常用命令了解过哪些?

  • 文件相关(mv mkdir cd ls)
  • 进程相关( ps top netstate )
  • 权限相关(chmod chown useradd groupadd)
  • 网络相关(netstat ip addr)
  • 测试相关(测试网络连通性:ping 测试端口连通性:telnet)

查找日志中某个字符的长度?

要在 Linux 中查找日志文件中某个字符的长度,你可以使用一些工具和命令来实现。其中,grep 是一个强大的命令行工具,可以用于在文件中查找匹配指定模式的行。你也可以结合一些其他命令来完成这个任务,如 awk 或者 sed。以下是一个示例命令,用于查找日志文件中某个字符的长度:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
grep "search_string" log_file | awk '{ print length }'

在这个命令中:

  1. grep "search_string" log_file 会找到日志文件中包含 "search_string" 的所有行。
  2. awk '{ print length }' 会输出每一行的长度,即搜索到的 "search_string" 的长度。

如果你想查找日志文件中单个字符的长度,你可以直接使用 grep 命令配合 wc 命令来实现:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
grep "c" log_file | wc -m

这个命令会计算搜索到的字符 "c" 在日志文件中出现的次数。

深拷贝和浅拷贝区别是什么?

image.png

  • 浅拷贝是指只复制对象本身和其内部的值类型字段,但不会复制对象内部的引用类型字段。换句话说,浅拷贝只是创建一个新的对象,然后将原对象的字段值复制到新对象中,但如果原对象内部有引用类型的字段,只是将引用复制到新对象中,两个对象指向的是同一个引用对象。
  • 深拷贝是指在复制对象的同时,将对象内部的所有引用类型字段的内容也复制一份,而不是共享引用。换句话说,深拷贝会递归复制对象内部所有引用类型的字段,生成一个全新的对象以及其内部的所有对象。

实现深拷贝的三种方法是什么?

在 Java 中,实现对象深拷贝的方法有以下几种主要方式:

实现 Cloneable 接口并重写 clone() 方法

这种方法要求对象及其所有引用类型字段都实现 Cloneable 接口,并且重写 clone() 方法。在 clone() 方法中,通过递归克隆引用类型字段来实现深拷贝。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class MyClass implements Cloneable {
    private String field1;
    private NestedClass nestedObject;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        MyClass cloned = (MyClass) super.clone();
        cloned.nestedObject = (NestedClass) nestedObject.clone(); // 深拷贝内部的引用对象
        return cloned;
    }
}

class NestedClass implements Cloneable {
    private int nestedField;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

使用序列化和反序列化

通过将对象序列化为字节流,再从字节流反序列化为对象来实现深拷贝。要求对象及其所有引用类型字段都实现 Serializable 接口。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import java.io.*;

class MyClass implements Serializable {
    private String field1;
    private NestedClass nestedObject;
    
    public MyClass deepCopy() {
        try {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(this);
            oos.flush();
            oos.close();
            
            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bis);
            return (MyClass) ois.readObject();
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
            return null;
        }
    }
}

class NestedClass implements Serializable {
    private int nestedField;
}

手动递归复制

针对特定对象结构,手动递归复制对象及其引用类型字段。适用于对象结构复杂度不高的情况。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class MyClass {
    private String field1;
    private NestedClass nestedObject;

    public MyClass deepCopy() {
        MyClass copy = new MyClass();
        copy.setField1(this.field1);
        copy.setNestedObject(this.nestedObject.deepCopy());
        return copy;
    }
}

class NestedClass {
    private int nestedField;

    public NestedClass deepCopy() {
        NestedClass copy = new NestedClass();
        copy.setNestedField(this.nestedField);
        return copy;
    }
}
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-05-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 小林coding 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
字节都到三面了,结果还是凉了。。。
今天给大家分享一个校招同学字节后端一二三面的面经,同学顺利通过一二面,可惜最后一轮三面的时候挂了。
小林coding
2024/04/02
2.7K0
字节都到三面了,结果还是凉了。。。
米哈游提前批,开始了!
这种方法要求对象及其所有引用类型字段都实现 Cloneable 接口,并且重写 clone() 方法。在 clone() 方法中,通过递归克隆引用类型字段来实现深拷贝。
小林coding
2024/07/16
2780
米哈游提前批,开始了!
京东后端实习一面
今天分享的是一位华中科技大学同学分享的京东一面面经,主要是一些非常基础的问题,也就是比较简单且容易准备的常规八股。
人不走空
2024/03/23
1370
京东后端实习一面
一文读懂Spring 循环依赖,写得太好了!(建议收藏)
如果笔者作为面试官,可能会问一些诸如“如果注入的属性为null,你会从哪几个方向去排查”这些场景题。
全栈程序员站长
2021/06/24
4340
被百度严格拷打 62 分钟,汗流浃背!
百度秋招提前批了已经开展 1 个月了,面试是 3 轮技术面 + 1 轮HR 面,已经有不少同学跟我反馈,已经面完百度三面了。
小林coding
2024/08/06
5700
被百度严格拷打 62 分钟,汗流浃背!
烂了大街的 Spring 循环依赖问题,你觉得自己会了吗
初学 Spring 的时候,我们就知道 IOC,控制反转么,它将原本在程序中手动创建对象的控制权,交由 Spring 框架来管理,不需要我们手动去各种 new XXX。
海星
2020/09/09
7360
Java面试:2021.05.28
大体来说,经历以下过程:接口需求调研、接口测试工具选择、接口测试用例编写、接口测试执行、接口测试回归、接口测试自动化持续集成。具体来说,接口测试流程分成以下九步:
夕梦
2021/06/03
3620
Java面试:2021.05.28
spring的三级缓存,以及循环依赖的形成和解决(详细)
在这个示例中,A和B相互依赖,因为A需要B才能被创建,而B又需要A才能被创建。这种情况下,Spring IoC容器会抛出一个异常,告诉你存在循环依赖。
一只牛博
2025/05/30
4100
spring的三级缓存,以及循环依赖的形成和解决(详细)
面向面试编程连载(二)
<font color = red>索引是为了加速对表中数据行的检索而创建的一种分散的存储结构</font>
疯狂的KK
2023/02/08
8090
面向面试编程连载(二)
不是说Spring解决了循环依赖问题么-为什么项目还报了循环依赖的异常
这里是TestA调用了TestB,TestB里面又调用了TestA。是一个典型的循环依赖场景,但是我们知道Spring对于循环依赖问题是做了处理的。但是这里为什么会报错?
Lvshen
2022/05/05
1.3K0
不是说Spring解决了循环依赖问题么-为什么项目还报了循环依赖的异常
爽玩多线程来开发,太哇塞了!
多线程大家肯定都不陌生,理论滚瓜烂熟,八股天花乱坠,但是大家有多少在代码中实践过呢?很多人在实际开发中可能就用用@Async,new Thread()。线程池也很少有人会自己去建,默认的随便用用。在工作中大家对于多线程开发,大多是用在异步,比如发消息,但是对于提效这块最重要的优势却很少有人涉及。因此本篇文章会结合我自己的工作场景带大家去发掘项目中的多线程场景,让你的代码快如闪电。
码猿技术专栏
2023/05/01
5730
爽玩多线程来开发,太哇塞了!
有自信了,再战阿里!
Record Lock 称为记录锁,锁住的是一条记录。而且记录锁是有 S 锁和 X 锁之分的:
程序员鱼皮
2023/09/06
2840
有自信了,再战阿里!
Spring解决循环依赖的思路竟然来自于一道算法题
「Spring」如何解决的循环依赖,是近两年流行起来的一道 Java 面试题。我今年也面试过很多自称“高级”的 Java 工程师,对循环依赖的回答多数都不是很理想,今天我们一起来学习学习它。
业余草
2021/12/06
5450
Spring解决循环依赖的思路竟然来自于一道算法题
这是璩静的简历,4条短视频丢了百度千万年薪的工作
在技术派实战项目中,很多地方都用到了 Redis,比如说用户活跃排行榜、作者白名单、常用热点数据(文章标签、文章分类)、计数统计(文章点赞收藏评论数粉丝数)等等。
沉默王二
2024/05/14
1850
这是璩静的简历,4条短视频丢了百度千万年薪的工作
Spring Ioc源码分析 之 Bean的加载(六):循环依赖处理
循环依赖,其实就是循环引用,就是两个或者两个以上的 bean 互相引用对方,最终形成一个闭环,如 A 依赖 B,B 依赖 C,C 依赖 A。如下图所示:
周同学
2019/10/14
7130
Spring Ioc源码分析 之 Bean的加载(六):循环依赖处理
Spring源码解析(五):循环依赖
在完成Bean的实例化后,属性注入之前Spring将Bean包装成一个工厂对象添加进了三级缓存中,对应源码如下:
Java微观世界
2025/01/21
5700
Spring源码解析(五):循环依赖
Spring高频面试题:如何解决循环依赖问题!
那Spring到底是如何解决的setter方法依赖注入引起的循环依赖问题呢?请看下图(其实主要是通过两个缓存来解决的):
会呼吸的Coder
2020/11/26
4.1K0
Spring高频面试题:如何解决循环依赖问题!
Spring三级缓存
在正式研究Spring如何解决循环依赖之前,不如我们就假设spring目前没有提供三级缓存来解决循环依赖,那么目前spring的getBean流程图就如下所示:
大忽悠爱学习
2022/06/12
7120
Spring三级缓存
Spring高频面试题:如何解决循环依赖问题!
那Spring到底是如何解决的setter方法依赖注入引起的循环依赖问题呢?请看下图(其实主要是通过两个缓存来解决的):
业余草
2020/11/26
5060
Spring高频面试题:如何解决循环依赖问题!
三级缓存
当你听到三级缓存的时候,你在想什么?你了解过的有哪些三级缓存?CPU三级缓存?Spring三级缓存?应用架构(JVM、分布式缓存、db)三级缓存?今天爬完香山,趁自己还不困的时候,把三级缓存的一些重点絮叨絮叨。
只喝牛奶的杀手
2023/04/07
8560
三级缓存
相关推荐
字节都到三面了,结果还是凉了。。。
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验