MQ之ActiveMQ
2020-04-03 18:43:37 3 举报仅支持查看
AI智能生成
Java
Java
模板推荐
作者其他创作
大纲/内容
Redis
为什么使用Redis?
为什么Redis是单线程的?有哪些好处?
简单口述
第一层面简单使用命令
String、List、Hash、Set、Zset
set、add、rpush
Redis 6.x版本,新增数据结构:GEO、Stream、BitMaps、Bitfiled、HyperLogLog
第二层面
了解主要数据结构底层实现
缓存失效机制
第三层面
集群的分片、复制、选举
单机
基本数据结构
string
key-valu键值对
list
数据结构
双向链表
压缩列表
为什么使用压缩列表?
节约内存
压缩列表的具体实现?
一个压缩列表可以包含任意多个节点(entry),每个节点可保存一个字节数组/一个整数值
entry
previous_entry_length
以字节为单位,记录压缩列表前一个节点的长度
encoding
记录节点content属性所保存数据类型及长度
content
保存节点的值
hash
数据结构
数组+链表
出现重复key,会把key放置到链表的头
rehash过程与原理
怎么实现的?实现过程简单描述?
Hash结构中存在2个hash数组,hash[0]、hash[1]
hash[0]
hash[1]
Redis与HashMap有哪些区别?
相同点
都使用链表法解决hash冲突
不同点
HashMap采用红黑树数据结构
HashMap全量式rehash、redis--hash是通过渐进式
怎么理解全量rehash?
四百万、四千万甚至四亿键值对,一次性将这些键全部rehash到hash[1],庞大计算量可能导致服务器停止服务
压缩列表中的每个节点,拥有3个属性,这3个属性记录了前一个节点的大小,及编码类型
渐近式rehash?
分而治之,通过添加、删除、查找、更新等操作rehash
set
zset
跳表
什么是跳表?为什么要用跳表?
10个元素的链表,每2个元素抽出一个
跳表与红黑树有什么区别?Redis为什么不适用红黑树而选用跳表?
持久化
RDB
描述
redis服务在启动时检测是否存在RDB文件,若存在就会自动载入RDB文件
RDB持久化通过保存数据库中键值对来记录数据库中的数据
实现
save
阻塞redis服务线程,直到RDB文件创建完毕为止,在服务起阻塞期间不能处理任何请求
bgsave
派生出一个子进程,由子进程负责创建RDB文件,服务起父进程继续处理命令请求
AOF
描述
AOF是通过保存Redis服务器执行的写命令(set、sadd、rpish)来记录数据库中数据
实现
命令追加append
文件写入
文件同步sunc
appendfsync always每提交一个修改命令都调用fsync刷新到AOF文件,非常非常慢,但也非常安全
appendfsync everysec每秒钟都调用fsync刷新到AOF文件,很快,但可能会丢失一秒以内的数据;
appendfsync no依靠OS进行刷新,redis不主动刷新AOF,这样最快,但安全性就差。默认并推荐每秒刷新,这样在速度和安全上都做到了兼顾
Redis过期策略(处理过期数据)
定时
每个设置过期时间的key都需要创建一个定时器,到过期时间就会立即清除。该策略可以立即清除过期的数据,对内存很友好;但是会占用大量的CPU资源去处理过期的数据,从而影响缓存的响应时间和吞吐量。
惰性
只有当访问一个key时,才会判断该key是否已过期,过期则清除。该策略可以最大化地节省CPU资源,却对内存非常不友好。极端情况可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存。
定期
每隔一定的时间,会扫描一定数量的数据库的expires字典中一定数量的key,并清除其中已过期的key。该策略是前两者的一个折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。<br>
过期策略用于处理过期的缓存数据,<span style="font-size: inherit;">内存淘汰策略用于处理内存不足时的需要申请额外空间的数据;</span><br>
Redis的内存淘汰策略(处理内存不足,清除可用空间)
noeviction:当内存不足以容纳新写入数据时,新写入操作会报错
allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key。
allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个key。
volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key。
volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key
volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的key优先移除。
集群
Redis槽分片
Redis怎么实现分片?
CRC16算法key产生的hash值,分配到16384
分片槽为什么设置位16834?
redis集群之间通信存在消息头和消息体,如果槽位为65536,发送心跳信息的消息头达8k,发送的心跳包过于庞大,集群过大时,占用网络带框
动态扩容、移除原理?
复制
主从复制(同步)
全同步
部分同步
哨兵
边界问题
选举机制
一主三从,master挂掉,会自动选举一个从服务为新的master
Redis集群方案
Redis集群自带槽节点
Redis Cluster中,Sharding采用slot槽的概念,分16384个槽,进入redis的键值对,根据key进行散列,分配到16384个slot中的某一个中,就是CRC16后16384取模
hash一致算法整合多个单机的redis服务(客户端分片)
什么是一致hash算法?
一致性Hash算法将整个哈希值空间组织成一个虚拟的圆环,如假设某哈希函数H的值空间为0-2^32-1(即哈希值是一个32位无符号整形)
优点
一致性Hash算法的容错性和可扩展性
缺点
Hash环的数据倾斜问题
解决方式
虚拟节点
连接
Sentinel哨兵机制
Sentinel(哨兵)是Redis的高可用性解决方案:由一个或多个Sentinel实例组成的Sentinel系统可以监视任意多个主服务器以及这些主服务器下的所有从服务器,并在被监视的主服务器进入下线状态时,自动将下线主服务器属下的某个从服务器升级为新的主服务器。<br>
twemproxy代理方案
Redis代理中间件twemproxy是一种利用中间件做分片的技术。twemproxy处于客户端和服务器的中间,将客户端发来的请求,进行一定的处理后(sharding),再转发给后端真正的redis服务器
什么情况下会导致整个集群不可用
有A,B,C三个节点的集群,在没有复制模型的情况下,如果节点B失败了,那么整个集群就会以为缺少5501-11000这个范围的槽而不可用
解决方式:主从,利用 选举机制
缓存数据(Redis)与数据库数据一致性
使用MySQL事务:1、先更新DB数据,再更新redis数据,若redis更新失败,回滚事务,否则提交事务
Redis集群启动
冷启动
redis 集群启动后,没有任何的缓存数据,可以称为 redis 的冷启动
热启动
缓存预热就是在 redis 启动的时候,就开始往 redis 中写了数据,然后再给应用提供服务
目前是怎么解决的?
GIS系统启动之后会有一个新的页面接口,获取省会城市的设备数据
优点:简单粗暴,易实现
缺点:扩展性差,手动触发繁琐
有没有更好的解决方法?
1、通过 nginx+lua 将访问流量上报到kafka
2、利用storm实时计算能力,从 kafka 中消费上报的流量日志数据,统计出商品对应访问次数
缓存穿透
缓存数据中没有,越过缓存去请求数据库
解决方法
通过用户认证、参数验证等,在上层拦截这些不合理的请求
当数据库查询结果为空时,数据也被缓存,但缓存有效期设置较短,以免影响正常数据的缓存
布隆过滤器
缓存击穿
部分热点数据到期,多线程访问,请求到数据库(时有发生,因为每时每刻都有缓存数据到期)
解决方法
缓存雪崩
大部分热点数据到期\缓存服务宕机,请求拥挤到数据库
解决方法
多节点、多服务
集群、哨兵主从
大key解决方案
string类型的big key,建议不要存入redis,用文档型数据库MongoDB代替或者直接缓存到CDN上等方式优化。有些 key 不只是访问量大,数据量也很大,这个时候就要考虑这个 key 使用的场景,存储在redis集群中是否是合理的,是否使用其他组件来存储更合适;如果坚持要用 redis 来存储,可能考虑迁移出集群,采用一主一备(或1主多备)的架构来存储。
单个简单的key存储的value很大<br>该对象需要每次都整存整取: 可以尝试将对象分拆成几个key-value, 使用multiGet获取值,这样分拆的意义在于分拆单次操作的压力,将操作压力平摊到多个redis实例中,降低对单个redis的IO影响;<br>该对象每次只需要存取部分数据: 可以像第一种做法一样,分拆成几个key-value,也可以将这个存储在一个hash中,每个field代表一个具体的属性,使用hget,hmget来获取部分的value,使用hset,hmset来更新部分属性。
hash, set,zset,list 中存储过多的元素<br>可以将这些元素分拆。以hash为例,原先的正常存取流程是 hget(hashKey, field) ; hset(hashKey, field, value)<br>现在,固定一个桶的数量,比如 10000, 每次存取的时候,先在本地计算field的hash值,模除 10000,确定了该field落在哪个key上。
子主题
Java EE
JDBC
EJB
JMS
JMS Provider
JMS Producer
JMS Consumer
JMS Message
消息头--类似于http请求头信息
消息体--类似于httpbody信息体
消息属性--消息中共有的一些属性信息或者去除重复信息的表示
Thrift RPC
什么是Thrift?有什么作用?为什么要用?有哪些好处?有哪些坏处?
Thrift架构
阻塞
非阻塞
单线程
多线程
总结:Thrift 服务器包含用于绑定协议和传输层的基础架构,它提供阻塞、非阻塞、单线程和多线程的模式运行在服务器上,可以配合服务器 / 容器一起运行,可以和现有的 J2EE 服务器 /Web 容器无缝的结合
传输协议
二进制
TBinaryProtocol —— 二进制编码格式进行数据传输
TCompactProtocol —— 高效率的、密集的二进制编码格式进行数据传输
文本
TJSONProtocol —— 使用 JSON 的数据编码协议进行数据传输
TSimpleJSONProtocol —— 只提供 JSON 只写的协议,适用于通过脚本语言解析
服务类型<br>
TSimpleServer —— 单线程服务器端使用标准的阻塞式 I/O
TThreadPoolServer —— 多线程服务器端使用标准的阻塞式 I/O
TNonblockingServer —— 多线程服务器端使用非阻塞式 I/O
Dubbo RPC
什么是Dubbo?有什么优势?
消息中间件MQ
用过什么MQ?
ActivceMQ
保证消息的可靠性
持久化
KahaDB
JDBC-MySQL
LevelDB
事务
签收
消息重复投递
消息重复投递超过默认6次,进入DLQ(死信队列)
幂等性问题
唯一索引
token
存储redis,利用消息的唯一id,为key
高可用性
避免单点故障--使用集群
主从配置
消息投递
异步投递
延时投递
定时投递
组成
RocketMQ
组成
Name Server 可集群部署,节点之间无任何信息同步。提供轻量级的服务发现和路由
Broker(消息中转角色,负责存储消息,转发消息) 部署相对复杂,Broker 分为Master 与Slave,一 个Master 可以对应多个Slave,但是一个Slave 只能对应一个Master,Master 与Slave 的对应关系通过 指定相同的BrokerName,不同的BrokerId来定 义,BrokerId为0 表示Master,非0 表示Slave。 Master 也可以部署多个。
Producer,生产者,拥有相同 Producer Group 的 Producer 组成一个集群, 与Name Server 集群 中的其中一个节点(随机选择)建立长连接,定期从Name Server 取Topic 路由信息,并向提供Topic 服务的Master 建立长连接,且定时向Master 发送心跳。Producer 完全无状态,可集群部署。
Consumer,消费者,接收消息进行消费的实例,拥有相同 Consumer Group 的 Consumer 组成 一个集群,与Name Server 集群中的其中一个节点(随机选择)建立长连接,定期从Name Server 取 Topic 路由信息,并向提供Topic 服务的Master、Slave 建立长连接,且定时向Master、Slave 发送心 跳。Consumer既可以从Master 订阅消息,也可以从Slave 订阅消息,订阅规则由Broker 配置决定。<br>
消息投递
NormalProducer(普通)
消息同步发送
消息发送出去后,producer会等到broker回应后才能继续发送下一个消息
子主题
消息异步发送
异步发送是指发送方发出数据后,不等接收方发回响应,接着发送下个数据包的通讯方式。 MQ 的异步 发送,需要用户实现异步发送回调接口(SendCallback)。消息发送方在发送了一条消息后,不需要等 待服务器响应即可返回,进行第二条消息发送。发送方通过回调接口接收服务器响应,并对响应结果进 行处理
单向(Oneway)
发送特点为发送方只负责发送消息,不等待服务器回应且没有回调函数触发,即只发 送请求不等待应答.效率最高
OrderProducer(顺序)
Push & Pull 模式
一种是Push模式(MQPushConsumer),即MQServer主动向消费端推送;另外一种是Pull模式(MQPullConsumer),即消费端在需要时,主动到MQServer拉取。
高可用
集群支持
单Master
多Master
多Master多Slave
每个Master配一个Slave,有多对Master-Slave,集群采用异步复制方式,主备 有短暂消息延迟,毫秒级
多Master多Slave
每个Master配一个Slave,有多对Master-Slave,集群采用同步双写方式,主备 都写成功,向应用返回成功
Topic、MessageQueue、Group关系?
Topic
一个Topic可以包含多个MessageQueue
MessageQueue
Group
保证消息的可靠性
持久化
事务
签收
消息重复投递
延迟的某个时间点 (默认是10秒,业务可设置),消息重复投递超过默认16次,进入DLQ(死信队列)
Kafka
为什么要使用?
消息系统
存储消息
kafka 把消息持久化到磁盘
流式处理平台
(自我介绍)自定义业务拓展实时计算
组成
Broker
Topic
Producer
Consumer
ConsumerGroup
Partition(分区)
为什么引入MQ?
解耦
消峰
异步
java中有队列为什么还要用消息队列?
Queue不能跨进程
不能在分布式系统中使用
没有持久化机制
引入MQ带来哪些问题?
系统可用性降低
原来是两个节点的通信,现在还需要独立运行一个服务,如果 MQ 服务器或者通信网络出现问题,就会导致请求失败。
系统复杂性增加
第一个就是你必须要理解相关的模型和概念,才 能正确地配置和使用 MQ。第二个,使用 MQ 发送消息必须要考虑消息丢失和消息重复 消费的问题。一旦消息没有被正确地消费,就会带来数据一致性的问题。
消息中间件保证数据一直性解决方案
Nginx
什么是nginx?有什么作用?为何要使用?有哪些好处?
nginx集群
nginx单机
内部模型
一个master进程
接收外界信号,如nginx -s reload 命令
管理监听worder进程,worker进程异常退出,重新new一个新的worker进程
多个worker进程
Spring
Spring
Spring DI
构造器注入
Setter方法注入
Spring装配
XML
Java
隐式Bean发现机制和自动装配
Compoent组建扫描
Autowired自动装配
Spring容器
Bean工厂
BeanFactory
应用上下文
5种应用上下文
Spring框架模块
Spring Profile
条件化Bean
自动装配歧异性
标示首选Bean,Primary<br>
@Scope作用域
单例
原型
会话
请求
运行时植入
注入外部的值
Spring表达式语言进行装配
AOP
通知
前
后
正常
异常
环绕通知
连接点
切点
Spring AOP
基于代理Spring AOP
动态代理
JDK 动态代理
JDK动态代理主要涉及java.lang.reflect包下边的两个类:Proxy和InvocationHandler。其中,InvocationHandler是一个接口,可以通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态地将横切逻辑和业务逻辑贬值在一起<br>
JDK动态代理的话,他有一个限制,就是它只能为接口创建代理实例
CGLib 动态代理
CGLib采用底层的字节码技术,全称是:Code Generation Library,CGLib可以为一个类创建一个子类,在子类中采用方法拦截的技术拦截所有父类方法的调用并顺势织入横切逻辑
区别
JDK动态代理是面向接口的
CGLib动态代理是通过字节码底层继承要代理类来实现
1、CGLib所创建的动态代理对象在实际运行时候的性能要比JDK动态代理高不少,有研究表明,大概要高10倍;(运行时性能)<br><br>2、但是CGLib在创建对象的时候所花费的时间却比JDK动态代理要多很多,有研究表明,大概有8倍的差距;(创建动态对象性能)<br><br>3、因此,对于singleton的代理对象或者具有实例池的代理,因为无需频繁的创建代理对象,所以比较适合采用CGLib动态代理,反正,则比较适用JDK动态代理。
最终的测试结果大致是这样的,在1.6和1.7的时候,JDK动态代理的速度要比CGLib动态代理的速度要慢,但是并没有教科书上的10倍差距,在JDK1.8的时候,JDK动态代理的速度已经比CGLib动态代理的速度快很多了
纯POJO切面
@Aspect注解驱动切面
注入Aspect切面
Spring 如何支持AOP?
什么是AOP?
spring MVC
Spring Boot
@SpringBootApplication
等价于3个Spring注解
@Configuration
@ComponentScan
@EnableAutoConfiguration<br>
SpringBoot run
webApplicationType
Spring Cloud
Spring Cloud Alibaba
nacos
服务注册和统一配置
sentinel
服务限流,服务降级
seata
分布式事务一致性
MySQL
概念
聚集索引
索引子节点存储的是当前表记录的行(当前行的数据),不需要再次回表查询
非聚集索引
索引子节点存储的是指向数据文件的内存地址--需要根据内存地址重新读取表数据(回表)
索引下推
一种优化技术,它可以减少回表操作的次数,从而提高查询性能。在MySQL中,当执行查询语句时,如果表中的列在索引中有覆盖(Covering Index)时,MySQL可以在索引中直接完成查询,而无需回表操作。这意味着MySQL可以使用索引中的条件来过滤数据,减少了IO开销和数据传输的成本
回表
在使用非聚簇索引进行查询时,MySQL需要通过索引找到对应的行,并进一步访问表中的其他列数据的过程。回表操作可能导致额外的IO操作,影响查询性能。
最左匹配原则
当使用联合索引(多列组成的索引)进行查询时,索引可以最大程度地利用的规则。根据最左匹配原则,查询语句可以有效地使用联合索引的左侧列来过滤数据,但是只能使用索引中连续的最左侧的列
覆盖索引
如果查询的字段都在索引中,称之为覆盖索引。覆盖索引可以避免对实际数据的访问,从而进一步提高查询效率。
事务
特性
原子性
一致性
隔离性
持久性
事务隔离级别
读未提交
最低的隔离级别,允许一个事务读取另一个事务尚未提交的数据。这种隔离级别存在脏读(Dirty Read)问题,即一个事务读取到了其他事务尚未提交的数据
读已提交
默认的隔离级别。它要求一个事务只能读取其他事务已经提交的数据,避免了脏读。但是,在该隔离级别下,可能存在不可重复读(Non-repeatable Read)问题,即在同一事务中,多次读取同一数据时,可能会得到不同的结果
可重复读
在该隔离级别下,确保同一事务中多次读取同一数据时,始终得到相同的结果。它通过在事务期间对读取的数据进行锁定来实现,以避免不可重复读。但是,在该隔离级别下,可能存在幻读(Phantom Read)问题,即在同一事务中,多次查询同一范围的数据时,可能会得到不同的结果。
串行化
最高的隔离级别,确保事务彼此完全隔离,避免脏读、不可重复读和幻读。它通过对读取和写入的数据进行严格的锁定来实现。串行化隔离级别通常会导致并发性能下降,因为它限制了并发访问数据库的能力
存储引擎
InnoDB
InnoDB是MySQL的默认存储引擎。它提供了事务支持和行级锁定,适用于需要高并发和数据完整性的应用。InnoDB还支持外键约束、崩溃恢复和自动增长列等功能
InnoDB索引结构----B+树特性
平衡性
B+树是一种自平衡的树结构,每个节点的子树的高度差不会超过1,保证了检索效率的稳定性
多路搜索
B+树是一棵多路搜索树,每个节点可以存储多个关键字和对应的数据指针,从而减少了磁盘I/O的次数,提高了查询效率
顺序访问
B+树的内部节点只存储关键字信息,数据存储在叶子节点中,叶子节点之间通过链表连接,可以支持范围查询和顺序访问
磁盘友好性
B+树的节点大小通常设置为磁盘页的大小(通常为4KB或8KB),使得每次读取或写入磁盘都是以页为单位,减少了磁盘IO的次数,提高了磁盘IO的效率
聚簇索引
在InnoDB中,B+树的叶子节点存储了完整的数据行,而非仅仅是索引键值。这种索引方式称为聚簇索引,可以加速主键的查找和范围查询
有序性
B+树的内部节点和叶子节点都按照关键字的大小进行排序,使得范围查询非常高效
支持快速插入和删除
由于B+树的平衡性和顺序访问性质,插入和删除操作相对高效,通常只需要几次磁盘IO
MyISAM
适用于读密集型应用。MyISAM不支持事务和行级锁定,但它支持全文搜索和压缩功能
日志
二进制日志(Binary Log)
二进制日志是MySQL中最重要的日志类型之一。它记录了所有对数据库的修改操作,包括插入、更新、删除等,以二进制的形式存储在磁盘上。二进制日志可以用于数据恢复、主从复制和数据库的故障恢复
事务日志(Transaction Log)
事务日志也称为重做日志(Redo Log),记录了事务的开始和结束,以及事务中对数据库的修改操作。事务日志用于保证事务的持久性和一致性,当数据库发生故障时,可以通过重做日志来恢复未提交的事务和修改操作
Undo Log(撤销日志)
Redo Log(重做日志)
错误日志(Error Log)
错误日志记录了MySQL服务器的错误和警告信息,包括启动过程中的错误、查询执行错误等。错误日志对于诊断和排查数据库问题非常有用。
查询日志(General Query Log)
查询日志记录了MySQL服务器接收到的所有查询请求,包括登录请求、DDL(数据定义语言)和DML(数据操作语言)语句等。查询日志可以用于性能分析和排查慢查询问题
慢查询日志(Slow Query Log)
慢查询日志记录了执行时间超过设定阈值的查询语句,用于分析和优化慢查询问题。慢查询日志可以配置执行时间的阈值和记录的详细程度。
中继日志(Relay Log)
中继日志用于MySQL的主从复制过程。主服务器将二进制日志发送给从服务器,从服务器将接收到的二进制日志写入中继日志,然后执行其中的操作。
实践问题
慢SQL排查思路
MySQL的慢查询日志
分析慢查询日志,可以找到执行时间较长的查询语句
数据库监控工具
如MySQL的Performance Schema、pt-query-digest等)可以实时监测查询的执行时间、资源消耗等指标,并帮助识别慢查询
数据库性能分析工具
使用性能分析工具(如Explain语句、Query Profiler等)来分析查询的执行计划和性能瓶颈
IDEA
Diagram生成UML图
代码格式化
设计模式
单例模式
原型模式
实现一个原型了类,需要3条件
实现 Cloneable 接口
重写 Object 类中的 clone 方法:
在重写的 clone 方法中调用 super.clone()
Java基础
静态和动态代理
静态编译期确定
动态运行期确定
用途
应用在各种框架
重写与重载
重载
一个类中,方法名相同、返回值相同,参数列表不同
重写
子类与父类中有同一个方法名的函数,子类重写改方法,并覆盖父类中的方法
形参和实参
形式参数
定义方法的时候使用的参数,是用来接收调用者传递的参数的
实际参数
调用时传递给方法的参数。实参在传递给别的方法之前是要被预先赋值的
值传递与引用传递区别?
值传递
调用方法时,将实际参数拷贝一份传递给方法,这样在方法中修改形式参数时,不会影响到实际参数
引用传递
也叫地址传递,是指在调用方法时,将实际参数的地址传递给方法,这样在方法中对形式参数的修改,将影响到实际参数
区别
值传递,传递的是副本。 引用传递,传递的是实际内存地址
Java中总结
Java参数传递中,不管传递的是基本数据类型还是引用类型,都是值传递
传递基本数据类型,比如原始类型(int、long、char等)、包装类型(Integer、Long、String等),实参和形参都是存储在不同的栈帧内,修改形参的栈帧数据,不会影响实参的数据
传参的引用类型,形参和实参指向同一个地址的时候,修改形参地址的内容,会影响到实参。当形参和实参指向不同的地址的时候,修改形参地址的内容,并不会影响到实参
参考链接--https://heapdump.cn/article/4859141
面向对象特性
封装
继承
类只能单继承多实现
接口可以多继承多个接口
问题:Java我为什么不支持多继承?
棱形继承
首先,不支持一个类继承多个类
其次,支持一个类可以实现多个接口
再次,一个接口可以继承多个接口
Java 8接口中支持默认函数(default),实现带有默认函数的类,需要重写默认函数
多态
运行期特性,Java的重写是多台的提现,有人提重载是一种静态多台的想法
面向对象5大基本原则
单一职责原则
一个类最好只做一件事
开放封闭原则
对扩展开放,对修改封闭
里氏替换原则
子类必须可以替换其基类
依赖倒置原则
程序要依赖与抽象接口,而不是具体实现
接口隔离原则
使用多个小的接口,而不是使用大的总接口
接口和抽象类区别,如何选择?
接口
用于指定规范
抽象类
为了复用
区别
接口里面之定义了方法,在不考虑Java8中default的情况下,接口是没有实现代码的
抽象类中,可以使用public、protected、default修饰符;而接口只能使用public
应用
模板方法模式
基本类型为什么还需要包装类
让基本类型拥有对象的特性,并添加了方法和属性,丰富基本类型操作
基本数据类型
byte
1字节
short
2字节
int
4字节
long
8字节
float
double
boolean
单个
4字节
布尔数组
1字节
char
2字节
装箱与拆箱
自动装箱
valueof()
自动拆箱
xxxValue()
String、StringBuilder、StringBuffer
String
不可变好处
缓存
字符串池
安全性
线程安全
hashcode缓存
性能
StringBuilder
非线程安全
StringBuffer
线程安全
常见字符编码
UTF-8
GBK
语法糖
计算机语言中添加的某种语法,对于语言的功能无影响,方面程序员使用
Lambda表达式实现方式
依赖底层的API,在编译阶段,编译器会把Lambda表达式进行解糖,转换成内部API的方式
Java泛型
通过类型擦除实现
好处
方便
安全
类型擦除
将泛型的代码转化为普通的Java代码,编译器更直接点,将泛型Java代码直接转换为普通Java字节码
反射
运行时能够获取自行的信息
动态代理的实现之一
反射为什么慢
动态解析类型,不能执行Java虚拟机优化,如JIT优化
参数需要包装成Objec[ ]类型
反射调用方法时,会从方法数组中遍历
方法可见性要检查
Java动态代理如何实现
JDK动态代理
被代理的类必须实现一个接口或者多个接口
InvocationHandler
Proxy
CgLib动态代理
是一个代码生成的类库,运行时生成某个类的子类
通过继承的方式实现的动态代理
Java注解
Java 注解用于为 Java 代码提供元数据。作为元数据,注解不直接影响你的代码执行,但也有一些类型的注解实际上可以用于这一目的。Java 注解是从 Java5 开始添加到 Java 的
注解分类
Java自带标准注解
@Override、@Deprecated、@SuppressWarnings等,使用这些注解后编译器就会进行检查
元注解
元注解也是Java自带的标准注解,只不过用于修饰注解,比较特殊
元注解是用于定义注解的注解,包括@Retention、@Target、@Inherited、@Documented、@Repeatable 等
自定义注解
用户可以根据自己的需求定义注解
Java序列化
将对象转换为可传输格式的数据
优秀的序列化框架
gson
fastjson
protobuf
异常分类
受检异常
一定要捕获或者向上抛出异常
非受检异常
一般是运行是异常,RunTimeException,不需要显示的捕获,若不捕获,运行期发生异常就会中断程序执行
Error和Exception
Throwable
Error
系统级错误,Java运行环境内部错误或硬件错误
举例
OutofMemoryError
StackOverflowError
Exception
表示程序需要捕获或处理的异常,分为RunTimeException和其他异常
RunTimeException
NullPointerException
ClassCastException
IllegalAragumentException
IllegalPathStateException
自定义异常
继承Exception
枚举
概念
一组常量构成的合法类型
关键字Enum
实现
字节码编译查看,定义了Final修饰的类,Final定义的属性
深拷贝、浅拷贝
深拷贝
将对象中的数据或元素,重新拷贝一份出来到新对象中,修改旧对象中的属性或元素不影响新对象中的数据
实现深拷贝
第三方工具如,fastjson、Apache common lang
重写clone方法,实现Cloneable
浅拷贝
与旧对象共享同一个内存地址
Java集合
List
ArrayList
1.5倍扩容
LinkedList
Vector
Set
HashSet
基于HashMap实现
根据HashCode和equal方法去重
TreeSet
基于TreeMap实现
根据CompareTo方法去重
LinkedHashSet
BitSet
Queue
Stack
Map
Hash冲突解决方式
开放地址法
冲突后,找下一个空的位置
链地址法
再哈希法
再次hash直到无冲突
建立公共溢出区
哈希表分为基本表和溢出表,将溢出部分放置到溢出表
HashMap
并发场景下问题汇总
JDK1.7--多线程并发扩容产生循环链表
多线程put时,size的个数和真正的个数不一样
多线程put时,可能值会覆盖掉
多线程get时,可能正在扩容,不能正常获取的key
HashTable
ConcurrentHashMap
value不能有null值
如何将集合变成线程安全
synchronize、ReentrantLock对代码加锁
ThreadLocal将集合放到线程内部访问
使用不可集合进行封装,当集合是不可变时,既是线程安全的
Collections.synchronizedxxx()方法
线程安全的集合
ConcurrentHashMap
ConcurrentLinkedDeque
ConcurrentLinkedQueue
ConcurrentSkipListMap
ConcurrentSkipSet
CopyOnWriteArrayList
CopyOnWriteArraySet
Stream
Java并发
线程安全的理解
指某个函数在多线程并发环境调用时,能正确处理多线程之间的共享变量,使程序正常执行完成
线程状态
初始New
运行Runnable
阻塞Blocked
等待Waiting
超时等待Timed_Waiting
终止Terminated
线程池状态
运行RUNNING
SHUTDOWN
STOP
任务已终止TIDYING
彻底终止TERMINATED
死锁
概念
两个或多个线程执行过程中,由于资源竞争造成阻塞的现象,若无外力作用,无法推进下去,系统处于死锁状态
死锁4个必要条件
互斥条件
占有且等待
不可强行占有
循环等待条件
Java内存模型(JMM)
概念
线程间变量的传递需要自己的工作内存与主内存之间进行数据同步
解决
JMM是一种规范,多线程通过共享内存通信,存在本地内存数据不一致、编译器会对代码指令重排序、处理器会对代码乱序执行带来的问题
并发编程原子性与数据库ACID原子性的区别
数据库中,原子性是“要么都执行,要么都回滚”
并发编程中,操作不可拆分,不被中断
synchronize
创建线程的方式
继承Thread类
实现Runnable接口
Callable和FutureTask
通过线程池
线程池
核心线程数
最大线程数
线程活跃时间
队列类型
拒绝策略
线程池线程数设定
CPU密集型应用
N+1
IO密集型应用
2N+1
最终设置还需要根据物理机、压测结果评估,并设置相对合理的值
ThreadLocal
initValue
set
get
remove
内存泄漏问题
ThreadLocalMap的生命周期跟Thread一样长,如果没有手动删除对应key就会导致内存泄漏,而不是因为弱引用
线程同步方式
概念
多线程之间按照顺序访问同一个共享资源,避免并发冲突导致的问题
synchronize
Reetrantlock
Semaphore
CountDownLatch
CyclicBarrier
synchronized
锁升级
无锁
偏向锁
当第一个线程访问对象同步块时,对象头Mark Word作为偏向锁标识
轻量级锁
重量级锁
原子性、可见性、有序性
volatile
可见性
lock前缀指令
有序性
内存屏障
有了synchronize为什么还需要volatile
sync是一种锁
有性能损耗
产生阻塞
volatile
禁止指令重排序
CAS-Compare And Swap
乐观锁技术
ABA问题
乐观锁版本号
Unsafe类
Java反射
JVM
性能调优
jps -l 查看所有java进程
jinfo
ps -mp 进程 -o THREAD,tid,time
将需要的线程id转换16进制格式
jstack 进程号 |grep tid -A60
编译
类编译加载过程
类编译
将 .java 文件编译成 .class 文件,才能在虚拟机上正常运行代码。文件的编译通常是由 JDK 中自带的 Javac 工具完成,一个简单的 .java 文件,我们可以通过 javac 命令来生成 .class 文件
编译后的字节码文件主要包括常量池和方法表集合这两部分
类加载
在类加载后,class 类文件中的常量池信息以及其它数据会被保存到 JVM 内存的方法区中
类连接
类在加载进来之后,会进行连接、初始化,最后才会被使用。在连接过程中,又包括验证、准备和解析三个部分。
验证
验证类符合 Java 规范和 JVM 规范,在保证符合规范的前提下,避免危害虚拟机安全
准备
为类的静态变量分配内存,初始化为系统的初始值。对于 final static 修饰的变量,直接赋值为用户的定义值。例如,private final static int value=123,会在准备阶段分配内存,并初始化值为 123,而如果是 private static int value=123,这个阶段 value 的值仍然为 0
解析
符号引用转为直接引用的过程
类初始化
类初始化阶段是类加载过程的最后阶段,在这个阶段中,JVM 首先将执行构造器 方法,编译器会在将 .java 文件编译成 .class 文件时,收集所有类初始化代码,包括静态变量赋值语句、静态代码块、静态方法,收集在一起成为 () 方法。
即时编译
在字节码转换为机器码的过程中,虚拟机中还存在着一道编译,那就是即时编译。
即时编译器类型
内置了两个 JIT,分别为 C1 编译器和 C2 编译器,这两个编译器的编译过程是不一样的。
C1
C1 编译器是一个简单快速的编译器,主要的关注点在于局部性的优化,适用于执行时间较短或对启动性能有要求的程序,例如,GUI 应用对界面启动速度就有一定要求。
C2
C2 编译器是为长期运行的服务器端应用程序做性能调优的编译器,适用于执行时间较长或对峰值性能有要求的程序。根据各自的适配性,这两种即时编译也被称为 Client Compiler 和 Server Compiler。
JIT(Just In Time)
热点检测
基于采样的方式探测
周期性检测线程方法栈顶,发现某个方法经常出现在栈顶,认为是热点方法
基于计数器的热点探测
为每个方法,甚至代码快建立计数器,超出阈值出发JIT
编译优化
逃逸分析
锁消除
锁膨胀
方法内联
空值检查清除
类型检查清除
公共子表达式消除
反编译
工具
jad
Javap,jdk自带的工具
cfr
方式
隔离Java程序
对Class文件进行加密
代码混淆
Java是编译型还是解释型
Java既是编译型,也是解析型,正常代码是解析执行的,JIT优化的过程是编译执行
对象可以作为GCRoot
Class
由系统类加载器加载的对象
Thread
活着的线程
Stack Local
Java方法的Local变量或参数
JNI Local
JNI Global
Monitor Used
被同步锁持有的对象
JMM
定义
Java Memory Model,Java内存模型,是一种规范,用于定义Java程序在多线程环境下,线程之间如何交互以及对共享内存的访问规则,JMM确保了多线程程序的可见性、原子性和有序性。
组成
主内存(Main Memory)
主内存是Java中所有线程共享的内存区域,包含了所有的变量和对象实例。所有的读写操作都要通过主内存进行
工作内存(Working Memory)
工作内存是线程私有的内存区域,用于存储线程使用的变量副本。线程在执行时,将主内存中的变量值复制到工作内存中进行操作,操作完成后再将结果刷新回主内存
内存交互操作(Memory Interactions)
内存交互操作是指线程之间进行通信的方式,包括读取和写入操作。读操作将变量的值从主内存复制到工作内存,写操作将工作内存中的值刷新回主内存。
原子性(Atomicity)
原子性是指对于一个操作或多个操作,要么全部执行成功,要么全部不执行,不会出现中间状态。Java中的原子操作包括基本的读取和写入操作,以及使用synchronized关键字或Lock接口实现的同步操作
可见性(Visibility)
可见性是指一个线程对共享变量的修改能够被其他线程及时感知到。Java中,通过volatile关键字和synchronized关键字实现了可见性。volatile关键字保证了变量的修改对其他线程是可见的,而synchronized关键字不仅提供了互斥访问的功能,还具备了可见性
有序性(Ordering)
有序性是指程序执行的结果按照一定的顺序保证,即保证线程之间的操作按照一定的顺序执行。Java中,使用volatile关键字和synchronized关键字可以确保有序性
JUC
AQS(抽象队列同步器)--AbstractQueuedSynchronizer
构成
用 volatile 修饰的整数类型的 state 状态,用于表示同步状态,提供 getState 和 setState 来操作同步状态;
提供了一个 FIFO 等待队列,实现线程间的竞争和等待,这是 AQS 的核心
AQS 内部提供了各种基于 CAS 原子操作方法,如 compareAndSetState 方法,并且提供了锁操作的acquire和release方法
Node节点内部类,前驱,后继,当前线程状态
AQS锁类型
独占锁
tryAcquire
tryRelease
共享锁
tryAcquireShared
tryReleaseShared
ReentrantLock
ReentrantReadWriteLock
StampedLock
当入队一个线程时,如果队尾是读结点,不会直接链接到队尾,而是链接到该读结点的cowait链中,cowait链本质是一个栈;<br><br>当入队一个线程时,如果队尾是写结点,则直接链接到队尾;<br><br>唤醒线程的规则和AQS类似,都是首先唤醒队首结点。区别是StampedLock中,当唤醒的结点是读结点时,会唤醒该读结点的cowait链中的所有读结点(顺序和入栈顺序相反,也就是后进先出)
Queue
ArrayBlockingQueue
LinkedBlockingQueue
SynchronousQueue
PriorityBlockingQueue
Nacos
Client
源码解析步骤
1、找到nacos-client端引入的包,并打开spring.factories文件<br><br>
2、打开查看对应加载Nacos相关的类
子主题
Service
Seata
实现思想--二阶段提交
AT--模式
Book
性能之巅
ElasticSearch
什么是ES
开源分布式搜索引擎,实现搜索、日志统计分析、实时监控
什么是文档和词条
ES中每条数据就是一个文档
对文档中的内容分词,就是词条,如“华为手机”分为“华为”和“手机”
正向索引和倒排索引
正向索引
什么是正向索引
基于文档id创建索引,查询词条是必须先找到文档,而后判断是否包含词条
倒排索引
什么是倒排索引
对文档内容分词,对词条创建索引,并记录词条所在文档信息。查询时先根据词条搜索文档id,再获取文档
ClickHouse
收藏
立即使用
Collect
Get Started
Collect
Get Started
Collect
Get Started
Collect
Get Started
评论
0 条评论
下一页