面试准备Java

该博客围绕Java开发展开,涵盖JVM架构、Zset底层实现、MySQL、集合、Redis、线程池、计算机网络、并发等知识。介绍了MySQL隔离级别、索引,Redis数据类型、持久化等,还提及线程池参数、并发锁机制以及负载均衡的分类和实现方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

1. JVM的架构(各个部分的功能)

2. Zset的底层实现

3.  MySQL

3.1 隔离级别(脏读、幻读、不可重复读)

3.2 MySQL的存储引擎

3.3 MySQL有哪些索引?

3.3.1 什么情况用到索引?

3.3.2 索引的底层实现

3.3.3 索引的事务ACID四大特性(底层如何实现ACID)

3.3.4 索引从数据结构上来看有哪些索引

3.4 MySQL有很大的数据量怎么办?怎么分表分库?

3.5 B+树了解吗?B+树的结构?底层呢?为什么这么用?

4. 集合

4.1 List、Set、Map有什么区别

4.2 Arraylist和LinkedList的区别及其优缺点

4.3 HashMap线程安全吗?为什么不安全?

4.4 HashMap的扩容机制

4.5 ConcurrentHashMap 为什么安全

5. Redis

5.1 Redis的基本数据类型(五大)

5.2 Redis的持久化方式(优缺点)

5.3 Redis的应用场景

5.4 Redis缓存穿透、缓存击穿、缓存雪崩

5.5 Redis数据淘汰策略

5.6  项目中如何保持Redis与数据库保持一致

5.7 Redis有事务吗?

5.8  Redis哨兵模式

5.7 AOF如果存储的文件满了要怎么处理

6. 线程池

6.1 讲讲线程池?为什么用线程池? 线程池的参数

6.2 进程、线程、协程

6.3 协程和线程的应用场景

7. 计算机网络

7.1 介绍一下计网里面的TCP和UDP协议

7.2 介绍一下http和https的区别?为什么https安全?

7.3 浏览器输入URL的解析流程

8. 并发

8.1 并发  讲讲悲观锁 乐观锁

8.2 了解过CAS吗?CAS如果发现与期望值不相等,会做什么操作?

8.3 RetreenLock是什么?

9. 其他

9.1 Spring源码看过吗?Spring的三级缓存知道吗?

9.2 抛开Spring,讲讲反射和动态代理?那三种代理模式怎么实现的?

9.3 谈谈对 I/O 的理解

9.4 谈谈对集合的理解

9.5 介绍面向对象编程思想

9.6. 重写和重载的区别

9.7 谈谈Nacos的理解

9.8 你怎么实现负载均衡的

9.9 Kafka和RabbitMQ RocketMQ的区别


1. JVM的架构(各个部分的功能)

方法区、堆内存、虚拟机栈、本地方法栈、程序计数器。

方法区

  • 方法区属于是 JVM 运行时数据区域的一块逻辑区域,是各个线程共享的内存区域。
  • 在不同的虚拟机实现上,方法区的实现是不同的。
  • 当虚拟机要使用一个类时,它需要读取并解析 Class 文件获取相关信息,再将信息存入到方法区。方法区会存储已被虚拟机加载的类信息、字段信息、方法信息、常量、静态变量、即时编译器编译后的代码缓存等数据

堆内存

  • 此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例以及数组都在这里分配内存。
  • Java 堆是垃圾收集器管理的主要区域,因此也被称作 GC 堆(Garbage Collected Heap)

虚拟机栈(栈)

  • Java 虚拟机栈也是线程私有的,它的生命周期和线程相同,随着线程的创建而创建,随着线程的死亡而死亡。 

本地方法栈

  • 虚拟机栈为虚拟机执行 Java 方法 (也就是字节码)服务,而本地方法栈则为虚拟机使用到的 Native 方法服务。 
  • 本地方法被执行的时候,在本地方法栈也会创建一个栈帧,用于存放该本地方法的局部变量表、操作数栈、动态链接、出口信息。

程序计数器

  • 程序计数器是一块较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器。字节码解释器工作时通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等功能都需要依赖这个计数器来完成。
  • 由于在JVM中,多线程是通过线程轮流切换来获得CPU执行时间的。因此,在任一具体时刻,一个CPU的内核只会执行一条线程中的指令。
  • 程序计数器是每个线程所私有的。为了能够使得每个线程都在线程切换后能够恢复在切换之前的程序执行位置,每个线程都需要有自己独立的程序计数器,并且不能互相被干扰,否则就会影响到程序的正常执行次序。
  • 只有程序计数器不会发生内存溢出现象(OutOfMemory):因为程序计数器中存储的数据所占空间的大小不会随程序的执行而发生改变。

2. Zset的底层实现

Zset和 Set 相比,Sorted Set 增加了一个权重参数 score,使得集合中的元素能够按 score 进行有序排列,还可以通过 score 的范围来获取元素的列表。有点像是 Java 中 HashMapTreeSet 的结合体。

3.  MySQL

3.1 隔离级别(脏读、幻读、不可重复读)

由于并发访问导致的数据读取问题

  1. 脏读:A事务读取B事务尚未提交的数据,此时如果B事务发生错误并执行回滚操作,那么A事务读取到的数据就是脏数据。
  2. 幻读:(前后多次读取,数据总量不一致)事务A在执行读取操作,需要两次统计数据的总量,前一次查询数据总量后,此时事务B执行了新增数据的操作并提交后,这个时候事务A读取的数据总量和之前统计的不一样,就像产生了幻觉一样,平白无故的多了几条数据,成为幻读。
  3. 不可重复读(前后多次读取,数据内容不一致)事务A在执行读取操作,由整个事务A比较大,前后读取同一条数据需要经历很长的时间 。而在事务A第一次读取数据,比如此时读取了小明的年龄为20岁,事务B执行更改操作,将小明的年龄更改为30岁,此时事务A第二次读取到小明的年龄时,发现其年龄是30岁,和之前的数据不一样了,也就是数据不重复了,系统不可以读取到重复的数据,成为不可重复读。

不可重复读和幻读到底有什么区别呢?

  • 1. 不可重复读是读取了其他事务更改的数据,针对update操作
  • 解决:使用行级锁,锁定该行,事务A多次读取操作完成后才释放该锁,这个时候才允许其他事务更改刚才的数据。
  • 2. 幻读是读取了其他事务新增的数据,针对insert和delete操作
  • 解决:使用表级锁,锁定整张表,事务A多次读取数据总量之后才释放该锁,这个时候才允许其他事务新增数据。
  • READ-UNCOMMITTED(读取未提交) :最低的隔离级别,允许读取尚未提交的数据变更。
  • READ-COMMITTED(读取已提交):允许读取并发事务已经提交的数据。
  • REPEATABLE-READ(可重复读) :对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改。
  • SERIALIZABLE(可串行化):最高的隔离级别,完全服从 ACID 的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。

3.2 MySQL的存储引擎

MySQL 支持多种存储引擎,通过 SHOW ENGINES 命令来查看 MySQL 支持的所有存储引擎

MySQL 存储引擎有 MyISAM 、InnoDB、Memory.......

3.2.1 InnoDB的特点

3.2.2 各存储引擎的缺点

3.3 MySQL索引

索引是一种用于快速查询和检索数据的数据结构,其本质可以看成是一种排序好的数据结构。索引的作用就相当于书的目录,帮助存储引擎快速获取数据的一种数据结构,形象的说就是索引是数据的目录。

3.3.1 什么情况用到索引?

什么时候适用索引?

  • 字段有唯一性限制的,比如商品编码;
  • 经常用于 WHERE 查询条件的字段,这样能够提高整个表的查询速度,如果查询条件不是一个字段,可以建立联合索引。
  • 经常用于 GROUP BY  ORDER BY 的字段,这样在查询的时候就不需要再去做一次排序了,因为我们都已经知道了建立索引之后在 B+Tree 中的记录都是排序好的。

什么时候不需要创建索引?

  • WHERE 条件,GROUP BYORDER BY 里用不到的字段,索引的价值是快速定位,如果起不到定位的字段通常是不需要创建索引的,因为索引是会占用物理空间的。
  • 字段中存在大量重复数据,不需要创建索引。比如性别字段,只有男女,如果数据库表中,男女的记录分布均匀,那么无论搜索哪个值都可能得到一半的数据。在这些情况下,还不如不要索引,因为 MySQL 还有一个查询优化器,查询优化器发现某个值出现在表的数据行中的百分比很高的时候,它一般会忽略索引,进行全表扫描。
  • 表数据太少的时候,不需要创建索引。
  • 经常更新的字段不用创建索引。比如不要对电商项目的用户余额建立索引,因为索引字段频繁修改,由于要维护 B+Tree的有序性,那么就需要频繁的重建索引,这个过程是会影响数据库性能的。
3.3.2 索引的底层实现
3.3.3 索引的事务ACID四大特性(底层如何实现ACID)

如何上锁了解过吗

3.3.4 索引从数据结构上来看有哪些索引

在 MySQL 中,MyISAM 引擎和 InnoDB 引擎都是使用 B+Tree 作为索引结构

按照数据结构维度划分:

  • B+Tree 索引:MySQL 里默认和最常用的索引类型。只有叶子节点存储 value,非叶子节点只有指针和 key。
  • 哈希索引:类似键值对的形式,一次即可定位。Hash 在做等值查询的时候效率贼快,搜索复杂度为 O(1)。Hash 表不适合做范围查询,它更适合做等值的查询,这也是 B+Tree 索引要比 Hash 表索引有着更广泛的适用场景的原因。
  • RTree 索引:一般不会使用,仅支持 geometry 数据类型,优势在于范围查找,效率较低,通常使用搜索引擎如 ElasticSearch 代替。
  • 全文索引:对文本的内容进行分词,进行搜索。目前只有 CHARVARCHARTEXT 列上可以创建全文索引。一般不会使用,效率较低,通常使用搜索引擎如 ElasticSearch 代替。

3.4 MySQL有很大的数据量怎么办?怎么分表分库?

  • Why:随着我们的系统运行,存储在关系型数据库的数据量会越来越大,系统的访问的压力也会随之增大,如果一个库中的表数据超过了一定的数量,比如说mysql中的表数据达到千万级别,就需要考虑进行分库分表;其次随着表数据的不断增大,会发现查询也随着变得缓慢,如果添加索引的话,会发现影响到了新增和删除的性能如果我们将数据库分散到不同的表上,单表的索引大小就得到了控制,对索引以及表结构的变更会变得很方便和高效;
  • How:将一个表结构分为多个表(垂直拆分),或者将一个表数据分片后放入多个表(水平拆分),这些表可以放在同一个数据库里,也可以放到不同的数据库中,甚至可以放到不同的数据库实例中。
  • 垂直拆分: 根据业务的维度,将原本一个库中的表拆分多个表,每个库中表与原有的结构不同
  • 水平拆分: 根据分片算法,将一个库拆分成多个库,每个库依旧保留原有的结构

3.5 B+树了解吗?B+树的结构?底层呢?为什么这么用?

4. 集合

4.1 List、Set、Map有什么区别

 HashSet、LinkedHashSet 和 TreeSet 三者的异同

  • HashSetLinkedHashSetTreeSet 都是 Set 接口的实现类,都能保证元素唯一,并且都不是线程安全的。
  • HashSetLinkedHashSetTreeSet 的主要区别在于底层数据结构不同。
  • HashSet 的底层数据结构是哈希表(基于 HashMap 实现)。
  • LinkedHashSet 的底层数据结构是链表和哈希表,元素的插入和取出顺序满足 FIFO。
  • TreeSet 底层数据结构是红黑树,元素是有序的,排序的方式有自然排序和定制排序。

4.2 Arraylist和LinkedList的区别及其优缺点

ArrayList 与 LinkedList 区别

  • 是否保证线程安全: ArrayListLinkedList 都是不同步的,也就是不保证线程安全;
  • 底层数据结构: ArrayList 底层使用的是数组LinkedList 底层使用的是 双向链表
  • 插入和删除是否受元素位置的影响:
    • ArrayList 采用数组存储,所以插入和删除元素的时间复杂度受元素位置的影响。 
    • LinkedList 采用链表存储,所以在头尾插入或者删除元素不受元素位置的影响

ArrayList 和 Array(数组)的区别

ArrayList 内部基于动态数组实现,比 Array(静态数组) 使用起来更加灵活:

  • ArrayList会根据实际存储的元素动态地扩容或缩容,而 Array 被创建之后就不能改变它的长度了。
  • ArrayList创建时不需要指定大小,而Array创建时必须指定大小。
  • ArrayList 允许你使用泛型来确保类型安全,Array 则不可以。
  • ArrayList 中只能存储对象。对于基本类型数据,需要使用其对应的包装类(如 Integer、Double 等)。Array 可以直接存储基本类型数据,也可以存储对象。
  • ArrayList 支持插入、删除、遍历等常见操作,并且提供了丰富的 API 操作方法,比如 add()remove()等。Array 只是一个固定长度的数组,只能按照下标访问其中的元素,不具备动态添加、删除元素的能力。

4.3 HashMap线程安全吗?为什么不安全?

  • 线程是否安全: HashMap 是非线程安全的,Hashtable 是线程安全的,因为 Hashtable 内部的方法基本都经过synchronized 修饰。
  • HashMap线程不安全的原因:
  • 在多线程环境下,HashMap 扩容时会造成死循环和数据丢失的问题。

  • 存在数据丢失问题:在 HashMap 中,多个键值对可能会被分配到同一个桶(bucket),并以链表或红黑树的形式存储。多个线程对 HashMapput 操作会导致线程不安全,具体来说会有数据覆盖的风险。

    • 例子1:两个线程 1,2 同时进行 put 操作,并且发生了哈希冲突。不同的线程可能在不同的时间片获得 CPU 执行的机会,当前线程 1 执行完哈希冲突判断后,由于时间片耗尽挂起。线程 2 先完成了插入操作。随后,线程 1 获得时间片,由于之前已经进行过 hash 碰撞的判断,所有此时会直接进行插入,这就导致线程 2 插入的数据被线程 1 覆盖了。
    • 例子2:线程 1 执行 if(++size > threshold) 判断时,假设获得 size 的值为 10,由于时间片耗尽挂起。线程 2 也执行 if(++size > threshold) 判断,获得 size 的值也为 10,并将元素插入到该桶位中,并将 size 的值更新为 11。随后,线程 1 获得时间片,它也将元素放入桶位中,并将 size 的值更新为 11。线程 1、2 都执行了一次 put 操作,但是 size 的值只增加了 1,也就导致实际上只有一个元素被添加到了 HashMap 中。

4.4 HashMap的扩容机制

4.5 ConcurrentHashMap 为什么安全

5. Redis

5.1 Redis的基本数据类型(五大)

String(字符串)、List(列表)、Set(集合)、Hash(散列)、Zset(有序集合)。

5.2 Redis的持久化方式(优缺点)

  • 使用缓存的时候,我们经常需要对内存中的数据进行持久化也就是将内存中的数据写入到硬盘中。大部分原因是为了之后重用数据(比如重启机器、机器故障之后恢复数据),或者是为了做数据同步(比如 Redis 集群的主从节点通过 RDB 文件同步数据)。
    • 快照(snapshotting,RDB)
    • 只追加文件(append-only file, AOF)
    • RDB 和 AOF 的混合持久化(Redis 4.0 新增)
  • 开启 AOF 持久化后每执行一条会更改 Redis 中的数据的命令,Redis 就会将该命令写入到 AOF 缓冲区 server.aof_buf 中,然后再写入到 AOF 文件中(此时还在系统内核缓存区未同步到磁盘),最后再根据持久化方式( fsync策略)的配置来决定何时将系统内核缓存区的数据同步到硬盘中。

AOF持久化操作:

  • 命令追加(append):所有的写命令会追加到 AOF 缓冲区中。
  • 文件写入(write):将 AOF 缓冲区的数据写入到 AOF 文件中。这一步需要调用write函数(系统调用),write将数据写入到了系统内核缓冲区之后直接返回了(延迟写)。注意!!!此时并没有同步到磁盘。
  • 文件同步(fsync):AOF 缓冲区根据对应的持久化方式( fsync 策略)向硬盘做同步操作。这一步需要调用 fsync 函数(系统调用), fsync 针对单个文件操作,对其进行强制硬盘同步,fsync 将阻塞直到写入磁盘完成后返回,保证了数据持久化。
  • 文件重写(rewrite):随着 AOF 文件越来越大,需要定期对 AOF 文件进行重写,达到压缩的目的。(AOF如果存储的文件满了要怎么处理)
    • AOF 重写机制是在重写时,读取当前数据库中的所有键值对,然后将每一个键值对用一条命令记录到「新的 AOF 文件」,等到全部记录完后,就将新的 AOF 文件替换掉现有的 AOF 文件。
    • 举个例子,在没有使用重写机制前,假设前后执行了「set name xiaolin」和「set name xiaolincoding」这两个命令的话,就会将这两个命令记录到 AOF 文件。在使用重写机制后,只记录set name xiaolincoding命令,之前的第一个命令就没有必要记录了。
  • 重启加载(load):当 Redis 重启时,可以加载 AOF 文件进行数据恢复。

5.3 Redis的应用场景

为什么要用到redis?redis为什么快? 仅仅因为redis访问数据快就使用redis吗?

为什么要用到Redis?

  • 访问速度更快
    • 传统数据库数据保存在磁盘,而 Redis 基于内存,内存的访问速度比磁盘快很多。引入 Redis 之后,我们可以把一些高频访问的数据放到 Redis 中,这样下次就可以直接从内存中读取,速度可以提升几十倍甚至上百倍。
  • 高并发
    • 一般像 MySQL 这类的数据库的 QPS 大概都在 4k 左右(4 核 8g) ,但是使用 Redis 缓存之后很容易达到 5w+,甚至能达到 10w+(就单机 Redis 的情况,Redis 集群的话会更高)。直接操作缓存能够承受的数据库请求数量是远远大于直接访问数据库的,所以我们可以考虑把数据库中的部分数据转移到缓存中去,这样用户的一部分请求会直接到缓存这里而不用经过数据库。进而,我们也就提高了系统整体的并发。
  • 功能全面
    • Redis 除了可以用作缓存之外,还可以用于分布式锁、限流、消息队列、延时队列等场景。
    • 分布式锁:通过 Redis 来做分布式锁是一种比较常见的方式。通常情况下,我们都是基于 Redisson 来实现分布式锁。

Redis为什么快?

  • Redis 基于内存,内存的访问速度比磁盘快很多;
  • Redis 基于 Reactor 模式设计开发了一套高效的事件处理模型,主要是单线程事件循环和 IO 多路复用(Redis 线程模式后面会详细介绍到);
  • Redis 内置了多种优化过后的数据类型/结构实现,性能非常高。
  • Redis 通信协议实现简单且解析高效。

5.4 Redis缓存穿透、缓存击穿、缓存雪崩

5.5 Redis数据淘汰策略

MySQL 里有 2000w 数据,Redis 中只存 20w 的数据,如何保证 Redis 中的数据都是热点数据?

  • Redis 通过一个叫做过期字典(可以看作是 hash 表)来保存数据过期的时间。过期字典的键指向 Redis 数据库中的某个 key(键),过期字典的值是一个 long long 类型的整数,这个整数保存了 key 所指向的数据库键的过期时间(毫秒精度的 UNIX 时间戳)
  • 如果假设你设置了一批 key 只能存活 1 分钟,那么 1 分钟后,Redis 是怎么对这批 key 进行删除的呢?常用的过期数据的删除策略:
  1. 惰性删除:只会在取出 key 的时候才对数据进行过期检查。这样对 CPU 最友好,但是可能会造成太多过期 key 没有被删除。
  2. 定期删除:每隔一段时间抽取一批 key 执行删除过期 key 操作。并且,Redis 底层会通过限制删除操作执行的时长和频率来减少删除操作对 CPU 时间的影响。
  • 定期删除对内存更加友好,惰性删除对 CPU 更加友好。两者各有千秋,所以 Redis 采用的是 定期删除+惰性/懒汉式删除
  • 但是,仅仅通过给 key 设置过期时间还是有问题的。因为还是可能存在定期删除和惰性删除漏掉了很多过期 key 的情况。这样就导致大量过期 key 堆积在内存里,然后就 Out of memory 了。
  • 怎么解决这个问题呢?
  • 答案就是:Redis 内存淘汰机制。

5.6  项目中如何保持Redis与数据库保持一致

 Cache Aside Pattern(旁路缓存模式):更新数据库,然后直接删除缓存 

5.7 Redis有事务吗?

  • Redis 事务提供了一种将多个命令请求打包的功能。然后,再按顺序执行打包的所有命令,并且不会被中途打断。
  • Redis事务不满足原子性和持久性。因此,考虑使用LUA脚本,来保证原子性。
  • 可以利用 Lua 脚本来批量执行多条 Redis 命令,这些 Redis 命令会被提交到 Redis 服务器一次性执行完成,大幅减小了网络开销。
  • 一段 Lua 脚本可以视作一条命令执行,一段 Lua 脚本执行过程中不会有其他脚本或 Redis 命令同时执行,保证了操作不会被其他指令插入或打扰

5.8  Redis哨兵模式

  • 当主服务器宕机后,需要手动把一台从服务器(Slave)切换为主服务器(Master),这就需要人工干预,费时费力,还会造成一段时间内服务不可用。
  • 手动调节不是一种推荐的方式,更多的时候我们有限考虑哨兵(Sentinel)模式。Redis从2.8开始正式提供了Sentinel(哨兵)架构来解决这个问题。
  • 哨兵模式相当于谋朝篡位的自动版,能够后台监控主机是否故障,如果故障了根据投票数自动将从库转换为主库。

6. 线程池

6.1 讲讲线程池?为什么用线程池? 线程池的参数

  • 线程池就是管理一系列线程的资源池。当有任务要处理时,直接从线程池中获取线程来处理,处理完之后线程并不会立即被销毁,而是等待下一个任务。
  • 池化技术的思想主要是为了减少每次获取资源的消耗,提高对资源的利用率。

  • 使用线程池的好处:

    • 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。

    • 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
    • 提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
  • 线程池参数:
    • corePoolSize : 核心线程数线程数定义了最小可以同时运行的线程数量。
    • maximumPoolSize : 当队列中存放的任务达到队列容量的时候,当前可以同时运行的线程数量变为最大线程数。
    • workQueue: 当新任务来的时候会先判断当前运行的线程数量是否达到核心线程数,如果达到的话,新任务就会被存放在队列中。

6.2 进程、线程

创建线程有很多种方式,例如继承Thread类、实现Runnable接口、实现Callable接口、使用线程池、使用CompletableFuture

6.3 线程的应用场景

7. 计算机网络

7.1 介绍一下计网里面的TCP和UDP协议

了解tcp/ip吗?tcp的三次握手过程,为什么挥手是四次,比握手要多一次

tcp udp 相关特性 什么时候用tcp/udp

tcp怎么保证可靠性

udp为什么快?除了快还有什么tcp没有的好处

tcp四次挥手,服务端断开连接后,客户端等待多久才结束连接?为什么?

7.2 介绍一下http和https的区别?为什么https安全?

7.3 浏览器输入URL的解析流程

8. 并发

8.1 并发  讲讲悲观锁 乐观锁

悲观锁:

  • 共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程
  • Java 中synchronizedReentrantLock等独占锁就是悲观锁思想的实现。
  • synchronized 是 Java 中的一个关键字,翻译成中文是同步的意思,主要解决的是多个线程之间访问资源的同步性,可以保证被它修饰的方法或者代码块在任意时刻只能有一个线程执行。

乐观锁:

  • 总是假设最好的情况,认为共享资源每次被访问的时候不会出现问题,线程可以不停地执行,无需加锁也无需等待,只是在提交修改的时候去验证对应的资源(也就是数据)是否被其它线程修改了。
  • 具体方法可以使用版本号机制或 CAS 算法。

8.2 了解过CAS吗?CAS如果发现与期望值不相等,会做什么操作?

  • CAS :就是用一个预期值和要更新的变量值进行比较,两值相等才会进行更新。
  • CAS 操作的"ABA"问题:如果一个变量 V 初次读取的时候是 A 值,并且在准备赋值的时候检查到它仍然是 A 值,能说明它的值没有被其他线程修改过了吗?很明显是不能的,因为在这段时间它的值可能被改为其他值,然后又改回 A,那 CAS 操作就会误认为它从来没有被修改过。ABA 问题的解决思路是在变量前面追加上版本号或者时间戳。

8.3 ReentrantLock是什么?

  • ReentrantLock 实现了 Lock 接口,是一个可重入且独占式的锁,和 synchronized 关键字类似。不过,ReentrantLock 更灵活、更强大,增加了轮询、超时、中断、公平锁和非公平锁等高级功能。
  • ReentrantLock 默认使用非公平锁。

9. 其他

9.1 Spring源码看过吗?Spring的三级缓存知道吗?

9.2 抛开Spring,讲讲反射和动态代理?那三种代理模式怎么实现的?

9.3 谈谈对 I/O 的理解

9.4 谈谈对集合的理解

9.5 介绍面向对象编程思想

9.6. 重写和重载的区别

9.7 你怎么实现负载均衡的

1. 负载均衡的定义

  • 将用户请求分摊到不同的服务器上处理,以提高系统整体的并发处理能力以及可靠性。
  • 负载均衡服务可以有由专门的软件(价格更便宜)或者硬件(性能更好)来完成。
  • 负载均衡是一种比较常用且实施起来较为简单的提高系统并发能力和可靠性的手段,不论是单体架构的系统还是微服务架构的系统几乎都会用到。

2. 负载均衡的分类

  • 负载均衡可以简单分为 服务端负载均衡 和 客户端负载均衡 这两种。 
  • 服务端负载均衡 主要应用在 系统外部请求 和 网关层 之间,可以使用软件/硬件实现。

  • 根据OSI七层模型,存在二三四七层负载均衡,最常见的是四层和七层负载均衡。

  • 四层负载均衡
    • 工作在 OSI 模型第四层,也就是传输层,这一层的主要协议是 TCP/UDP。
    • 负载均衡器在这一层能够看到数据包里的源端口地址以及目的端口地址,会基于这些信息通过一定的负载均衡算法将数据包转发到后端真实服务器。
    • 四层负载均衡的核心就是 IP+端口层面的负载均衡,不涉及具体的报文内容。
  • 七层负载均衡
    • 工作在 OSI 模型第七层,也就是应用层,这一层的主要协议是 HTTP 。
    • 这一层的负载均衡比四层负载均衡路由网络请求的方式更加复杂,它会读取报文的数据部分(比如说我们的 HTTP 部分的报文),然后根据读取到的数据内容(如 URL、Cookie)做出负载均衡决策。
    • 七层负载均衡器的核心是报文内容(如 URL、Cookie)层面的负载均衡,执行第七层负载均衡的设备通常被称为 反向代理服务器
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值