├── docs
├── 微信.png
├── Jyannis.jpg
├── IO
│ ├── docs
│ │ ├── 五种IO模型.jpg
│ │ ├── 异步IO模型.png
│ │ ├── 阻塞IO模型.png
│ │ ├── BIO基本原理.png
│ │ ├── Selector.png
│ │ ├── 信号驱动IO模型.png
│ │ ├── 多路复用IO模型.png
│ │ └── 非阻塞IO模型.png
│ ├── 一文读懂BIO、NIO、AIO.md
│ └── 操作系统层面的IO.md
├── redis
│ ├── docs
│ │ ├── 重启加载.png
│ │ ├── AOF工作流程.png
│ │ ├── AOF阻塞流程.png
│ │ └── RDB工作流程.png
│ ├── 常见缓存问题及其解决.md
│ ├── 概述.md
│ ├── 数据结构.md
│ ├── 持久化.md
│ └── 常用命令.md
├── qrcode_1586150172794.jpg
├── tech-life
│ ├── 200412-简历-专业技能.png
│ ├── 200412-简历-项目经历-范例1.png
│ ├── 200412-简历-项目经历-范例2.png
│ └── 今天我收到了蚂蚁金服实习offer.md
├── Spring
│ ├── Spring AOP.md
│ └── Spring IOC.md
└── JVM
│ └── JMM Java内存模型.md
└── README.md
/docs/微信.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jyannis/JavaLearning/HEAD/docs/微信.png
--------------------------------------------------------------------------------
/docs/Jyannis.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jyannis/JavaLearning/HEAD/docs/Jyannis.jpg
--------------------------------------------------------------------------------
/docs/IO/docs/五种IO模型.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jyannis/JavaLearning/HEAD/docs/IO/docs/五种IO模型.jpg
--------------------------------------------------------------------------------
/docs/IO/docs/异步IO模型.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jyannis/JavaLearning/HEAD/docs/IO/docs/异步IO模型.png
--------------------------------------------------------------------------------
/docs/IO/docs/阻塞IO模型.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jyannis/JavaLearning/HEAD/docs/IO/docs/阻塞IO模型.png
--------------------------------------------------------------------------------
/docs/IO/docs/BIO基本原理.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jyannis/JavaLearning/HEAD/docs/IO/docs/BIO基本原理.png
--------------------------------------------------------------------------------
/docs/IO/docs/Selector.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jyannis/JavaLearning/HEAD/docs/IO/docs/Selector.png
--------------------------------------------------------------------------------
/docs/IO/docs/信号驱动IO模型.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jyannis/JavaLearning/HEAD/docs/IO/docs/信号驱动IO模型.png
--------------------------------------------------------------------------------
/docs/IO/docs/多路复用IO模型.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jyannis/JavaLearning/HEAD/docs/IO/docs/多路复用IO模型.png
--------------------------------------------------------------------------------
/docs/IO/docs/非阻塞IO模型.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jyannis/JavaLearning/HEAD/docs/IO/docs/非阻塞IO模型.png
--------------------------------------------------------------------------------
/docs/redis/docs/重启加载.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jyannis/JavaLearning/HEAD/docs/redis/docs/重启加载.png
--------------------------------------------------------------------------------
/docs/redis/docs/AOF工作流程.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jyannis/JavaLearning/HEAD/docs/redis/docs/AOF工作流程.png
--------------------------------------------------------------------------------
/docs/redis/docs/AOF阻塞流程.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jyannis/JavaLearning/HEAD/docs/redis/docs/AOF阻塞流程.png
--------------------------------------------------------------------------------
/docs/redis/docs/RDB工作流程.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jyannis/JavaLearning/HEAD/docs/redis/docs/RDB工作流程.png
--------------------------------------------------------------------------------
/docs/qrcode_1586150172794.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jyannis/JavaLearning/HEAD/docs/qrcode_1586150172794.jpg
--------------------------------------------------------------------------------
/docs/tech-life/200412-简历-专业技能.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jyannis/JavaLearning/HEAD/docs/tech-life/200412-简历-专业技能.png
--------------------------------------------------------------------------------
/docs/tech-life/200412-简历-项目经历-范例1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jyannis/JavaLearning/HEAD/docs/tech-life/200412-简历-项目经历-范例1.png
--------------------------------------------------------------------------------
/docs/tech-life/200412-简历-项目经历-范例2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jyannis/JavaLearning/HEAD/docs/tech-life/200412-简历-项目经历-范例2.png
--------------------------------------------------------------------------------
/docs/redis/常见缓存问题及其解决.md:
--------------------------------------------------------------------------------
1 | 缓存雪崩、穿透、击穿是每个学习缓存使用的人都应该熟知的问题。
2 |
3 | **目录**
4 |
5 | - [缓存雪崩](#缓存雪崩)
6 | - [缓存穿透](#缓存穿透)
7 | - [缓存击穿](#缓存击穿)
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | ## 缓存雪崩
16 |
17 | #### 概念
18 |
19 | 缓存同一时间大面积的失效,所以,后面的请求都会落到数据库上,造成数据库短时间内承受大量请求而崩掉。
20 |
21 |
22 |
23 | #### 解决方案
24 |
25 | - 在批量往**Redis**存数据的时候,把每个Key的失效时间都加个随机值
26 | - 如果**Redis**是集群部署,将热点数据均匀分布在不同的**Redis**库中也能避免全部失效的问题
27 | - 设置热点数据永远不过期,有更新操作就更新缓存
28 | - 选择合适的内存淘汰策略
29 |
30 |
31 |
32 |
33 |
34 | ## 缓存穿透
35 |
36 | #### 概念
37 |
38 | 缓存穿透说简单点就是大量请求的 key 根本不存在于缓存中,导致请求直接到了数据库上,根本没有经过缓存这一层。
39 |
40 |
41 |
42 | #### 解决方案
43 |
44 | - 添加参数校验,权限校验,对不合法情况进行过滤
45 | - 如果有越过缓存直接访问数据库的请求,就为它添加值为null的key。但这样可能导致key过多
46 | - 利用布隆过滤器(把数据库里有的数据添加到布隆过滤器里,请求数据库前先查过滤器看看有没有)
47 |
48 |
49 |
50 |
51 |
52 | ## 缓存击穿
53 |
54 | #### 概念
55 |
56 | **缓存击穿**是指一个Key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个Key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,导致数据库崩掉。
57 |
58 |
59 |
60 | #### 解决方案
61 |
62 | - 设置热点数据永不过期
63 | - 也可以考虑对访问数据的代码块加上互斥锁(性能一般不太好)
--------------------------------------------------------------------------------
/docs/redis/概述.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Redis概述中包括了一些我们需要知道的Redis相关的基本概念,例如它的优点、内容、使用场景、如何开启关闭等等。
6 |
7 | **目录**
8 |
9 | - [Redis为什么速度快](#Redis为什么速度快)
10 | - [Redis有哪些数据结构](#Redis有哪些数据结构)
11 | - [Redis可以做什么(使用场景)](#Redis可以做什么)
12 | - [Redis是否支持事务](#Redis是否支持事务)
13 | - [如何启动Redis](#如何启动Redis)
14 | - [Redis的关闭过程](#Redis的关闭过程)
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | ## Redis为什么速度快
23 |
24 | 1. 内存数据库
25 |
26 | Redis是基于内存的,而我们都知道内存访问速度远快于磁盘访问。
27 |
28 | 2. IO多路复用技术(使用epoll)
29 |
30 | **多路复用**是网络IO中非常重要的一个概念,由于篇幅有限这里就不多赘述了,希望不太了解的读者要自行好好查阅。
31 |
32 | 3. 单线程架构
33 |
34 | Redis是单线程的架构,这意味着没有多线程的上下文切换开销。
35 |
36 |
37 |
38 |
39 |
40 | ## Redis有哪些数据结构
41 |
42 | - 字符串String
43 | - 基于字符串还演变出位图Bitmaps和HyperLogLog
44 | - 哈希Hash
45 | - 列表List
46 | - 集合Set
47 | - 有序集合Zset
48 |
49 |
50 |
51 |
52 |
53 | ## Redis可以做什么
54 |
55 | 1. 一般缓存
56 |
57 | 使用字符串String
58 |
59 | 2. 排行榜
60 |
61 | 使用有序集合Zset
62 |
63 | 3. 计数器
64 |
65 | 使用字符串String
66 |
67 | 4. 社交网络
68 |
69 | 使用集合Set
70 |
71 | 5. 消息队列
72 |
73 | 一般不用。有专门的消息队列例如RocketMQ等,它们具有更好的封装性,功能更强大。
74 |
75 |
76 |
77 |
78 |
79 | ## Redis是否支持事务
80 |
81 | Redis 通过 MULTI(进入事务)、EXEC(执行)、WATCH 等命令来实现事务(transaction)功能。事务提供了一种将多个命令请求打包,然后一次性、按顺序地执行多个命令的机制,并且在事务执行期间,服务器不会中断事务而改去执行其他客户端的命令请求,它会将事务中的所有命令都执行完毕,然后才去处理其他客户端的命令请求。
82 |
83 | 但Redis不支持回滚。Redis命令在事务中可能会执行失败,但是Redis事务不会回滚,而是继续会执行余下的命令。设计为“不支持回滚”是因为:
84 |
85 | - 只有当发生语法错误了(这个问题在命令队列时无法检测到),Redis命令才会执行失败。这意味着这些都是程序性错误,这类错误在开发的过程中就能够发现并解决掉,几乎不会出现在生产环境。
86 | - 由于不需要回滚,这使得Redis内部更加简单,而且运行速度更快。
87 |
88 | 可以参考:[Redis事务详解](https://baijiahao.baidu.com/s?id=1613631210471699441&wfr=spider&for=pc)
89 |
90 |
91 |
92 |
93 |
94 | ## 如何启动Redis
95 |
96 | 最好是通过配置文件.conf启动,具有良好的灵活性。
97 |
98 | 另外还有两种方式可以启动Redis,但是都不如通过配置文件启动好,只要知道可以修改.conf配置文件然后让Redis按照我们想要的配置来启动即可。
99 |
100 |
101 |
102 |
103 |
104 | ## Redis的关闭过程
105 |
106 | 优雅的关闭方式:使用redis-cli shutdown:
107 |
108 | 1. 断开与客户端的连接
109 | 2. 持久化文件生成(默认是RDB)
110 |
111 | 如果直接kill -9强制杀死Redis服务,不但不会做持久化操作,还会造成缓冲区等资源不能被优雅关闭,极端情况会造成AOF和复制数据丢失的情况。这和Redis宕机是一样的。
112 |
113 |
114 |
115 |
--------------------------------------------------------------------------------
/docs/redis/数据结构.md:
--------------------------------------------------------------------------------
1 | Redis有五大数据结构。
2 |
3 | **目录**
4 |
5 | - [字符串String](#字符串String)
6 | - [哈希Hash](#哈希Hash)
7 | - [列表List](#列表List)
8 | - [集合Set](#集合Set)
9 | - [有序集合Zset](#有序集合Zset)
10 |
11 |
12 |
13 |
14 |
15 | ## 字符串String
16 |
17 | #### 内部编码
18 |
19 | 字符串类型的内部编码有三种:
20 |
21 | - int:8个字节的长整型
22 | - embstr:小于等于39个字节的字符串
23 | - raw:大于39个字节的字符串
24 |
25 | Redis根据当前值的类型和长度决定使用哪种编码实现。
26 |
27 |
28 |
29 | #### 使用场景
30 |
31 | 1. 缓存
32 | 2. 计数
33 | 3. 多应用服务的Session共享
34 |
35 |
36 |
37 |
38 |
39 | ## 哈希Hash
40 |
41 | #### 内部编码
42 |
43 | 哈希类型的内部编码有两种:
44 |
45 | - ziplist压缩列表
46 |
47 | - - 哈希类型元素个数小于512个,同时所有值都小于64字节时使用ziplist
48 | - ziplist使用紧凑结构实现多个元素的连续存储,在节省内存方面优秀,但操作时间长
49 |
50 | - hashtable哈希表
51 |
52 | - - 不满足ziplist条件时使用hashtable
53 |
54 |
55 |
56 | #### 使用场景
57 |
58 | 和字符串String基本相似。
59 |
60 |
61 |
62 |
63 |
64 | ## 列表List
65 |
66 | #### 内部编码
67 |
68 | 列表类型的内部编码有两种(3.2版本之前):
69 |
70 | - ziplist压缩列表
71 |
72 | - - 哈希类型元素个数小于512个,同时所有值都小于64字节时使用ziplist
73 | - ziplist使用紧凑结构实现多个元素的连续存储,在节省内存方面优秀,但操作时间长
74 |
75 | - linkedlist链表
76 |
77 | - - 不满足ziplist条件时使用linkedlist
78 |
79 | 列表类型的内部编码有两种(3.2版本之后):
80 |
81 | - 元素较少较小时使用ziplist
82 | - 多则使用quicklist
83 |
84 | **Quicklist**:
85 |
86 | 纯粹的使用 Linkedlist, 也就是普通链表来存储数据有两个弊端:
87 |
88 | 1. 每个节点都有自己的前后指针,指针所占用的内存有点多,太浪费了。
89 | 2. 每个节点单独的进行内存分配,当节点过多,造成的内存碎片太多了。影响内存管理的效率。
90 |
91 | 因此,定义了 quicklist, 将 linkedlist 和 ziplist 结合起来,形成一个 将多个 ziplist 通过前后指针互相连接起来的结构,可以在一定程度上缓解上面说到的两个问题。
92 |
93 |
94 |
95 | #### 使用场景
96 |
97 | 1. 消息队列
98 | 2. 文章列表
99 |
100 |
101 |
102 |
103 |
104 | ## 集合Set
105 |
106 | #### 内部编码
107 |
108 | 集合类型的内部编码有两种:
109 |
110 | - intset整数集合
111 |
112 | - - 集合中元素都是整数,且个数小于512时使用intset
113 |
114 | - hashtable哈希表
115 |
116 | - - 不满足intset条件时使用hashtable
117 |
118 |
119 |
120 | #### 使用场景
121 |
122 | 1. 标签(用户兴趣标签等)
123 |
124 |
125 |
126 |
127 |
128 | ## 有序集合Zset
129 |
130 | #### 内部编码
131 |
132 | 有序集合类型的内部编码有两种:
133 |
134 | - ziplist压缩列表
135 |
136 | - - 元素个数小于128个,同时所有值都小于64字节时使用ziplist
137 |
138 | - skiplist跳跃表
139 |
140 | - - 不满足ziplist条件时使用skiplist
141 |
142 |
143 |
144 | #### 使用场景
145 |
146 | 1. 排行榜(用户赞数等)
--------------------------------------------------------------------------------
/docs/redis/持久化.md:
--------------------------------------------------------------------------------
1 | Redis支持两种持久化机制:RDB和AOF。
2 |
3 | **目录**
4 |
5 | - [RDB](#RDB)
6 | - [工作流程](#工作流程)
7 | - [优缺点](#优缺点)
8 | - [AOF](#AOF)
9 | - [工作流程](#工作流程)
10 | - [文件格式](#文件格式)
11 | - [追加阻塞](#追加阻塞)
12 | - [重启加载](#重启加载)
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | ## RDB
21 |
22 | RDB持久化是把当前进程数据生成快照保存到硬盘的过程。
23 |
24 | #### 工作流程
25 |
26 | 
27 |
28 | Redis进程执行fork操作创建子进程,RDB持久化过程由子进程负责,完成后自动结束。阻塞只发生在fork阶段,一般时间很短。
29 |
30 |
31 |
32 | #### 优缺点
33 |
34 | **优点**
35 |
36 | - RDB是一个紧凑压缩的二进制文件,代表某个时间点上的数据快照,非常适合备份、全量复制等场景;
37 | - 恢复速度快,远快于AOF。
38 |
39 | **缺点**
40 |
41 | - 由于RDB是生成当时的快照,不能每时每刻都生成,没办法做到实时持久化。
42 |
43 |
44 |
45 |
46 |
47 | ## AOF
48 |
49 | AOF(append only file)持久化:以独立日志的方式记录每次写命令,重启时再重新执行AOF文件中的命令达到恢复数据的目的。
50 |
51 | #### 工作流程
52 |
53 | 
54 |
55 | 1. 所有写入命令追加到aof_buf(AOF缓冲)中;
56 | 2. AOF缓冲区根据对应的策略向硬盘做同步操作;
57 | 3. 随着AOF文件越来越大,需要定期对AOF文件进行重写,达到压缩的目的;
58 | 4. 当Redis服务器重启时,可以加载AOF文件进行数据恢复。
59 |
60 | tips:**为什么需要aof_buf,而不是直接写磁盘?**
61 |
62 | 直接写磁盘导致性能完全取决于磁盘负载。先写入缓冲区的话,我们可以根据实际需求**调整缓冲区同步硬盘的策略**,在性能和安全方面做出平衡。
63 |
64 |
65 |
66 | #### 文件格式
67 |
68 | AOF命令写入的内容直接是文本协议格式。例如set hello world这条命令,在AOF缓冲区会追加如下文本:
69 |
70 | `\r\n$3\r\nset\r\n$5\r\nhello\r\n$5\r\nworld\r\n`
71 |
72 | 直接采取文本协议格式的优势如下:
73 |
74 | - 更好的兼容性
75 | - 更好的可读性
76 | - 避免二次处理开销(在存储格式和可处理格式之间转换)
77 |
78 |
79 |
80 | #### 同步策略
81 |
82 | Redis支持三种缓冲区同步文件策略(也就是上文说的**缓冲区同步硬盘的策略**),
83 |
84 | 由参数appendfsync控制,不同值的含义如下:
85 |
86 | | **可配置值** | **说明** |
87 | | ------------ | ------------------------------------------------------------ |
88 | | always | 每次命令写入aof_buf都会调用系统fsync同步 |
89 | | **everysec** | **写入aof_buf后调用系统write。由专门线程负责每秒调用fsync** |
90 | | no | 写入aof_buf后调用系统write,不进行fysnc。由操作系统自行控制fsync |
91 |
92 | 默认everysec,它也是一般推荐的配置。其他两个不太实用。
93 |
94 | tips:
95 |
96 | - 系统**write**操作
97 |
98 | write操作在写入系统缓冲区后直接返回,同步硬盘操作依赖于操作系统调度机制。
99 |
100 | - 系统**fsync**操作
101 |
102 | fsync操作针对单个文件操作(例如AOF文件)做强制硬盘同步。fsync将阻塞直到写入硬盘完成后返回,确保数据持久化。
103 |
104 |
105 |
106 | #### 追加阻塞
107 |
108 | 
109 |
110 | 1. 主线程负责写入AOF缓冲区
111 |
112 | 2. AOF线程负责每秒执行一次fsync,并记录最近一次同步时间
113 |
114 | 3. 主线程负责对比上次AOF同步时间:
115 |
116 | 4. 1. 如果距上次同步成功时间在2秒内,主线程直接返回;
117 | 2. 如果距上次同步成功时间超过2秒,主线程将阻塞,直到同步完成。
118 |
119 | tips:也就是说,在everysec配置下,系统发生宕机时,最多可能丢失2秒数据,而不是1秒。
120 |
121 |
122 |
123 |
124 |
125 | ## 重启加载
126 |
127 | AOF和RDB都可以用于服务器重启时的数据恢复。流程如下:
128 |
129 | 
--------------------------------------------------------------------------------
/docs/Spring/Spring AOP.md:
--------------------------------------------------------------------------------
1 | 框架是JAVA面试中比较重要的一个部分,而Spring是面试最爱问的框架之一。Spring中最重要的就是IOC和AOP这两大概念,本篇中对于AOP相关知识及面试题作一个总结。
2 |
3 |
4 |
5 | **目录**
6 |
7 | - [基本概念](#基本概念)
8 | - [性质](#性质)
9 | - [目的](#目的)
10 | - [原理](#原理)
11 | - [AspectJ静态代理](#AspectJ静态代理)
12 | - [SpringAOP动态代理](#SpringAOP动态代理)
13 | - [AOP关键词](#AOP关键词)
14 | - [动态代理的两种实现](#动态代理的两种实现)
15 | - [JDK动态代理](#JDK动态代理])
16 | - [CGLIB动态代理](#CGLIB动态代理)
17 | - [其他一些核心问题](#其他一些核心问题)
18 |
19 |
20 |
21 |
22 |
23 | ## AOP基本概念
24 |
25 | AOP:Aspect Oriented Programming 也即**面向切面编程**
26 |
27 | 下面分三个方面来陈述:
28 |
29 | - 性质:AOP本质上是一个什么东西。
30 | - 目的:AOP之所以存在,是为了解决什么问题?
31 | - 原理:AOP背后是如何实现的?
32 |
33 |
34 |
35 |
36 |
37 | ### 性质
38 |
39 | AOP是一种**设计思想**。
40 |
41 | 就像OOP是面向对象编程一样,AOP则是OOP的一种延伸,是针对切面进行编程。
42 |
43 | 那么什么叫**切面**呢?可以认为是一个可重用的模块,或是一系列公共行为和逻辑。
44 |
45 | 如果具体举例子来说,比如权限认证(用户在进入任何功能性接口之前都应该先完成鉴权)、日志打印(许多接口出现问题的时候都应该有相应的异常日志)等等。
46 |
47 |
48 |
49 |
50 |
51 | ### 目的
52 |
53 | 借助AOP,我们希望:
54 |
55 | - 通过尽可能地复用代码,以降低编码复杂度
56 | - 通过把切面提取出来,以降低模块间的耦合度
57 | - 联系上面这两条,同时也就提升了系统的可维护性
58 |
59 |
60 |
61 |
62 |
63 | ### 原理
64 |
65 | AOP实现的关键在于**代理模式**,AOP代理主要分为静态代理和动态代理。静态代理的代表为AspectJ;动态代理则以Spring AOP为代表。
66 |
67 | #### AspectJ静态代理
68 |
69 | AspectJ会在编译阶段生成AOP代理类,因此也称为编译时增强。
70 |
71 | 它会在编译阶段将Aspect(切面)织入到Java字节码中,运行的时候利用到的就是增强之后的AOP对象。
72 |
73 | #### SpringAOP动态代理
74 |
75 | Spring AOP不会像AspectJ那样在编译时增强,而是每次在运行时于内存中临时为方法生成一个AOP对象。
76 |
77 | 这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。
78 |
79 | 如果理解了动态代理,那么对Spring AOP动态代理也不会难以理解。希望对动态代理不是很熟悉的读者可以先自行查阅相关资料学习。
80 |
81 |
82 |
83 |
84 |
85 | ## AOP关键词
86 |
87 | 在Java AOP的世界里,有一些关键词是一定要明白的。
88 |
89 |
90 |
91 | **Aspect(切面)**
92 |
93 | Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。
94 |
95 | **Joint point(连接点)**
96 |
97 | 表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 joint point。
98 |
99 | **Pointcut(切点)**
100 |
101 | 表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方。
102 |
103 | **Advice(增强)**
104 |
105 | Advice 定义了在 Pointcut 里面定义的程序点具体要做的操作,它通过 before、after 和 around 来区别是在每个 joint point 之前、之后还是代替执行的代码。
106 |
107 | **Advice执行顺序(很重要!!)**
108 |
109 | 环绕(@Around)开始阶段 → @Before → 执行切点方法 → 环绕结束阶段 → @After → @AfterReturning
110 |
111 | **Target(目标对象)**
112 |
113 | 织入 Advice 的目标对象。
114 |
115 | **Weaving(织入)**
116 |
117 | 将 Aspect 和其他对象连接起来, 并创建 Adviced object 的过程。
118 |
119 |
120 |
121 |
122 |
123 | ## 动态代理的两种实现
124 |
125 | 在上文的阅读中,我们知道Spring AOP是基于动态代理实现的。
126 |
127 | 它背后其实有两种实现方式:JDK动态代理和CGLIB动态代理。这也是非常重要的知识点,在下文中详细说明。
128 |
129 |
130 |
131 | ### JDK动态代理
132 |
133 | 利用拦截器(拦截器必须实现InvocationHanlder)加上反射机制生成一个实现代理接口的匿名类,
134 |
135 | 在调用具体方法前调用InvokeHandler来处理。
136 |
137 | https://blog.csdn.net/yhl_jxy/article/details/80586785
138 |
139 |
140 |
141 | ### CGLIB动态代理
142 |
143 | 利用ASM开源包,修改代理对象类的class文件的字节码,生成子类,再加载class文件。
144 |
145 | https://blog.csdn.net/yhl_jxy/article/details/80633194
146 |
147 |
148 |
149 |
150 |
151 | ### 其他一些核心问题
152 |
153 | #### 何时使用JDK还是CGLIB?
154 |
155 | - 如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP。
156 |
157 | - 如果目标对象实现了接口,可以强制使用CGLIB实现AOP。
158 |
159 | - 如果目标对象没有实现了接口,必须采用CGLIB库,Spring会自动在JDK动态代理和CGLIB之间转换。
160 |
161 |
162 |
163 | #### JDK动态代理和CGLIB字节码生成的区别?
164 |
165 | - JDK动态代理只能对实现了接口的类生成代理,而不能针对类。
166 |
167 | - CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,
168 |
169 | 并覆盖其中方法实现增强,但是因为采用的是继承,所以该类或方法最好不要声明成final,
170 |
171 | 对于final类或方法,是无法继承的。private也是一样。
172 |
173 |
174 |
175 | #### CGlib比JDK快?
176 |
177 | - 使用CGLib实现动态代理,CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,在jdk6之前比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类。
178 |
179 | - 在jdk6、jdk7、jdk8逐步对JDK动态代理优化之后,在调用次数较少的情况下,JDK代理效率高于CGLIB代理效率,只有当进行大量调用的时候,jdk6和jdk7比CGLIB代理效率低一点,但是到jdk8的时候,jdk代理效率高于CGLIB代理,总之,每一次jdk版本升级,jdk代理效率都得到提升,而CGLIB代理消息确有点跟不上步伐。
180 |
181 |
182 |
183 | #### Spring如何选择用JDK还是CGLIB?
184 |
185 | - 当Bean实现接口时,Spring就会用JDK的动态代理。
186 |
187 | - 当Bean没有实现接口时,Spring使用CGlib实现。
188 |
189 | - 可以强制使用CGlib(在spring配置中加入)。
--------------------------------------------------------------------------------
/docs/IO/一文读懂BIO、NIO、AIO.md:
--------------------------------------------------------------------------------
1 | 在上篇[IO(上)——基本概念](https://mp.weixin.qq.com/s?__biz=MzA4Mjk5OTA0OQ==&mid=2247483976&idx=1&sn=f6206aebd57292948b82cd38c8ac4071&chksm=9ffc602ea88be938b7f53337c48a1bab8b04242777f2867f443c604cc8278f592ec7b1f8df1d&token=2111105882&lang=zh_CN#rd)中,我们初步认识了同步异步、阻塞非阻塞的含义,并了解了与之对应的IO模型。
2 |
3 | 本篇文章中我们将这些基本概念具体化,探讨JAVA实现层面的IO。
4 |
5 | 在JAVA层面,我们需要熟悉的IO有三种:BIO,NIO,AIO。
6 |
7 | 本篇中,我们主要从“是什么”、“有什么用”、“怎么实现的”三个角度切入分析。
8 |
9 | 是什么——也即“基本概念”。
10 |
11 | 有什么用——也即“适用场景”。
12 |
13 | 有什么用——也即“基本原理”。
14 |
15 |
16 |
17 |
18 |
19 | ## BIO(Blocking I/O)
20 |
21 | ### 基本概念
22 |
23 | 同步阻塞I/O模式,数据的读取写入必须阻塞在一个线程内等待其完成。它是JDK4之前的唯一选择。
24 |
25 |
26 |
27 | ### 使用场景
28 |
29 | 在连接数比较少的情况下,它还是很有用的,因为编码起来很方便,思路很简单清晰。
30 |
31 | 但是对于十万级百万级连接会有些吃力。
32 |
33 |
34 |
35 | ### 基本原理
36 |
37 | 采用 **BIO 通信模型** 的服务端,通常由一个独立的 Acceptor 线程负责监听客户端的连接。
38 |
39 | 我们通过自旋的方式监听请求,一旦接收到一个连接请求,就会建立通信套接字(Socket)进行读写。
40 |
41 | 此时不能再接收其他连接请求,只能等待同当前连接下的操作执行完成。
42 |
43 | 不过可以通过多线程来支持多个客户端的连接,如下图所示。
44 |
45 | 
46 |
47 | 如果要让 **BIO 通信模型** 能够同时处理多个请求,就必须使用多线程,也就是在接收到连接请求后为每个请求创建一个新的线程进行处理。
48 |
49 | 处理完成之后,通过输出流返回应答给客户端,线程销毁。
50 |
51 | 考虑到如果某个连接还没有释放,但又不做任何事情的话就会造成不必要的线程开销,因而可以通过 **线程池机制** 改善。传送门:[线程池知识总结](https://mp.weixin.qq.com/s?__biz=MzA4Mjk5OTA0OQ==&mid=2247483763&idx=1&sn=73501f177d4882a23401e614b7a06b5d&chksm=9ffc6315a88bea030635a6fc138fe4d41b10a2801c04f7eefdbe642b0317d5a9de98e627edef&scene=21#wechat_redirect)
52 |
53 |
54 |
55 |
56 |
57 | ## NIO(NEW I/O)
58 |
59 | ### 基本概念
60 |
61 | JDK4开始支持。同步非阻塞I/O,表面上总体和BIO类似,但使用了多路复用技术,一个线程可以监听多个IO操作请求。
62 |
63 |
64 |
65 | 以下是一些分析性文本:
66 |
67 | NIO是同步的多路复用IO。在上篇[IO(上)——基本概念](https://mp.weixin.qq.com/s?__biz=MzA4Mjk5OTA0OQ==&mid=2247483976&idx=1&sn=f6206aebd57292948b82cd38c8ac4071&chksm=9ffc602ea88be938b7f53337c48a1bab8b04242777f2867f443c604cc8278f592ec7b1f8df1d&token=2111105882&lang=zh_CN#rd)中,我们提到网上经常称NIO为Non-Blocking,也就是非阻塞IO,但是同步和非阻塞是无法同时满足的。那这到底是为什么呢?
68 |
69 | 这是因为NIO的非阻塞,指的是对于一个IO请求来看,是非阻塞的。NIO采取多路复用,一个线程可能会负责多个IO请求,在一个IO请求处理完后,线程并不会阻塞在哪里,而是可能去处理其他的IO请求。所以称之为非阻塞。
70 |
71 | > tip:如果不了解IO多路复用,可能对上面这段话无法理解,请先继续看下去。
72 |
73 |
74 |
75 | ### 使用场景
76 |
77 | NIO适用于IO连接请求数目多但连接时间普遍较短的业务场景。
78 |
79 |
80 |
81 | ### 基本原理
82 |
83 | #### IO多路复用(multiplex)
84 |
85 | 我们先用通俗的话来讲一下多路复用是什么,这里借鉴一下知乎上不错的一篇回答(我有稍加修改):
86 |
87 | > 假设你是一个老师,让30个学生解答一道题目,然后检查学生做的是否正确,你有下面几个选择:
88 | >
89 | > \1. 第一种选择:**按顺序逐个检查**,先检查A,然后是B,之后是C、D。。。这中间如果有一个学生卡主,全班都会被耽误。
90 | >
91 | > 这种模式就好比,你用循环挨个处理socket,根本不具有并发能力。(诶个进行BIO)
92 | >
93 | > \2. 第二种选择:你**创建30个分身**,每个分身检查一个学生的答案是否正确。 这种类似于为每一个用户创建一个进程或者线程处理连接。(用三十个线程同时去做BIO)
94 | >
95 | > \3. 第三种选择,你**站在讲台上等,谁解答完谁举手**。这时C、D举手,表示他们解答问题完毕,你下去依次检查C、D的答案,然后继续回到讲台上等。此时E、A又举手,然后去处理E和A……
96 | >
97 | > 这种就是IO多路复用。
98 | >
99 | >
100 | >
101 | > 作者:柴小喵
102 | > 链接:https://www.zhihu.com/question/28594409/answer/52835876
103 |
104 |
105 |
106 | #### NIO核心组件
107 |
108 | NIO核心组件包括:Channel通道、Buffer缓冲、Selector选择器。利用这三个核心组件,构成新IO的强大优势。
109 |
110 | - Channel
111 | - Buffer
112 | - Selector
113 |
114 |
115 |
116 | ##### 通道(Channel)
117 |
118 | NIO 通过Channel(通道) 进行读写。
119 |
120 | 就好像流操作都要基于Stream一样,NIO中的所有I/O操作都基于Channel对象。
121 |
122 | 一个Channel代表和某一实体(文件、网络套接字Socket等)的连接。
123 |
124 | 通过Channel,可以读取数据到Buffer中,也可以把Buffer中的数据写入通道。
125 |
126 | | **区别** | **Stream** | **Channel** |
127 | | ------------------ | -------------- | ------------------------------------------------ |
128 | | 支持异步 | 不支持 | 支持 |
129 | | 是否可双向传输数据 | 不能,只能单向 | 可以,既可以从通道读取数据,也可以向通道写入数据 |
130 | | 是否结合Buffer使用 | 不 | 必须结合Buffer使用 |
131 | | 性能 | 较低 | 较高 |
132 |
133 |
134 |
135 | ##### 缓冲(Buffer)
136 |
137 | NIO中所使用的缓冲区是封装过的Buffer类,而不是简单的byte数组等等。
138 |
139 | 借助Buffer提供的API,我们可以灵活地操作数据。
140 |
141 | **Buffer本质上就是一块内存区**,可以用来写入数据,并在稍后读取出来。在NIO类库中加入Buffer对象,体现了新库与原I/O的一个重要区别。在面向流的I/O中,可以将数据直接写入或者将数据直接读到 Stream 对象中。虽然 Stream 中也有 Buffer 开头的扩展类,但只是流的包装类,还是从流读到缓冲区,而 NIO 却是直接读到 Buffer 中进行操作。
142 |
143 | 最常用的缓冲区是 ByteBuffer,一个 ByteBuffer 提供了一组功能用于操作 byte 数组。除了ByteBuffer,还有其他的一些缓冲区。每一种Java基本类型(除了Boolean)都对应有一种缓冲区。
144 |
145 |
146 |
147 | ##### 选择器(Selector)
148 |
149 | NIO的IO多路复用是利用选择器实现的,它是Java NIO核心组件之一。
150 |
151 | Selector用于采集各个Channel的状态(或者说事件)。
152 |
153 | 我们将多个Channel注册到Selector中,由一个线程进行监听,当事件触发时再进行处理,以实现IO多路复用。
154 |
155 | 这些事件包含:
156 |
157 | - Accept:有可接受的连接
158 | - Connect:连接成功
159 | - Read:有数据可读
160 | - Write:可以写入数据
161 |
162 | 
163 |
164 |
165 |
166 |
167 |
168 | ## AIO(Asynchronous I/O)
169 |
170 | ### 基本概念
171 |
172 | JDK7开始支持。异步I/O,基于事件和回调机制实现。
173 |
174 |
175 |
176 | ### 使用场景
177 |
178 | AIO适用于IO连接请求数目多且连接时间往往较长的业务场景。
179 |
180 |
181 |
182 | ### 基本原理
183 |
184 | JAVA AIO框架在windows下使用windows IOCP技术,在Linux下使用epoll多路复用IO技术模拟异步IO。
185 |
186 | 在JAVA AIO框架中,由于应用程序不是轮询方式,而是事件触发和回调的方式,所以不再需要选择器(Selector)了,改由通道(Channel)直接到操作系统注册监听。
187 |
188 | JAVA NIO和JAVA AIO框架,除了因为操作系统的实现不一样而去掉了Selector外,其他的重要概念都是存在的,例如上文中提到的Channel的概念,还有基于Buffer缓冲处理。
--------------------------------------------------------------------------------
/docs/IO/操作系统层面的IO.md:
--------------------------------------------------------------------------------
1 | 在上篇[IO(中)——深入理解JAVA层面的IO](https://mp.weixin.qq.com/s?__biz=MzA4Mjk5OTA0OQ==&mid=2247483980&idx=1&sn=1f7ae34cf2dc93b919b68dc5c90be443&chksm=9ffc602aa88be93c21fd2c298950e3c080d6a5384af906fd2f654209e61e9cee0a6a7667fce2&token=1958345249&lang=zh_CN#rd)中,我们认识了JAVA对于IO的封装:BIO、NIO、AIO。
2 |
3 | 而我们也知道,Java中提供的IO有关的API,在进行处理的时候,其实是依赖操作系统层面的IO操作实现的。
4 |
5 | 本篇文章中我们就继续探索下去,了解一下在Linux下的五种IO模型:
6 |
7 | - 阻塞IO模型
8 | - 非阻塞IO模型
9 | - 多路复用IO模型
10 | - 信号驱动IO模型
11 | - 异步IO模型
12 |
13 | > tips:
14 | >
15 | > 在下文中提到的“应用程序”、“用户进程”、“进程”普遍都指同一个概念。
16 |
17 |
18 |
19 |
20 |
21 | ## 阻塞IO模型
22 |
23 | ### 基本含义
24 |
25 | 和[IO(中)——深入理解JAVA层面的IO](https://mp.weixin.qq.com/s?__biz=MzA4Mjk5OTA0OQ==&mid=2247483980&idx=1&sn=1f7ae34cf2dc93b919b68dc5c90be443&chksm=9ffc602aa88be93c21fd2c298950e3c080d6a5384af906fd2f654209e61e9cee0a6a7667fce2&token=1958345249&lang=zh_CN#rd)中提到的BIO含义相似。
26 |
27 | 进程等待某个条件,如果条件不满足,则一直等下去。条件满足,则进行下一步操作。
28 |
29 |
30 |
31 | ### 操作流程
32 |
33 | 
34 |
35 | 1. 应用程序通过系统调用`recv`或`recvfrom`尝试接收数据
36 | 2. 内核开始准备数据,其间应用程序**阻塞等待**
37 | 3. 内核准备好数据,并将数据从**内核空间**复制到**用户空间**
38 | 4. 应用程序处理数据
39 |
40 |
41 |
42 | > 阻塞IO模型下,应用程序可能耗费在等待上的时间过长,浪费了许多性能。
43 | >
44 | > 那么如何减少等待时间呢?就引入了非阻塞IO模型。
45 |
46 |
47 |
48 |
49 |
50 | ## 非阻塞IO模型
51 |
52 | ### 基本含义
53 |
54 | 与**阻塞IO模型**的不同之处在于,在非阻塞IO模型下,应用程序在等待数据时是采取**轮询**的方式,而不是**阻塞**的方式。
55 |
56 | 这就可以类比到JAVA里Synchronized悲观锁和CAS乐观锁的区别。
57 |
58 |
59 |
60 | ### 操作流程
61 |
62 | 
63 |
64 | 如果理解了前面的阻塞IO模型,对于现在这个非阻塞IO模型应该也很好理解。
65 |
66 | 应用程序轮询内核是否已准备好数据,直到准备好后对数据进行处理。
67 |
68 | 非阻塞的优势就在于,在连续两次轮询之间,应用程序可以去做一些别的事情。
69 |
70 |
71 |
72 | > 非阻塞IO模型下,应用程序耗费在等待上的时间少了一些。
73 | >
74 | > 但高频度的轮询,依然可能导致性能有比较大的损耗。
75 | >
76 | > 那能不能不由应用程序反复去问内核,而是由内核主动通知应用程序呢?
77 | >
78 | > 这就引入了信号驱动IO模型。
79 |
80 |
81 |
82 |
83 |
84 | ## 信号驱动IO模型
85 |
86 | ### 基本含义
87 |
88 | 信号驱动IO模型下,以通知而非轮询的方式完成数据的等待,然后再由应用程序完成对数据的处理。
89 |
90 |
91 |
92 | ### 操作流程
93 |
94 | 
95 |
96 | 1. 应用程序向内核注册一个信号处理程序
97 |
98 | 注册是一个及时返回的操作,不会产生阻塞。
99 |
100 | 2. 内核准备好数据,对应用程序递交信号
101 | 3. 应用程序通过系统调用`recv`或`recvfrom`尝试接收数据
102 | 4. 内核将数据从内核空间复制到用户空间(期间应用程序阻塞)
103 | 5. 数据复制完成,应用程序开始处理数据
104 |
105 |
106 |
107 | > 信号驱动IO模型下,已经最大化节省了等待的时间。
108 | >
109 | > 但是由于需要引入信号,在实现复杂性上就会稍高一些。
110 | >
111 | > 事实上我们发现,还有更好的方式来提升性能,这就引入了下面的多路复用IO模型。
112 |
113 |
114 |
115 |
116 |
117 | ## 多路复用IO模型
118 |
119 | ### 基本含义
120 |
121 | 和[IO(中)——深入理解JAVA层面的IO](https://mp.weixin.qq.com/s?__biz=MzA4Mjk5OTA0OQ==&mid=2247483980&idx=1&sn=1f7ae34cf2dc93b919b68dc5c90be443&chksm=9ffc602aa88be93c21fd2c298950e3c080d6a5384af906fd2f654209e61e9cee0a6a7667fce2&token=1958345249&lang=zh_CN#rd)中提到的NIO含义相似。
122 |
123 | 多路复用IO模型下,多个应用程序(即多个进程)的IO注册到同一个管道里,由管道代替它们与内核交互。当内核准备好了任何一个应用程序的数据,就由管道通知应用程序去接收。
124 |
125 |
126 |
127 | ### 操作流程
128 |
129 | 
130 |
131 | 1. 应用程序注册到select上
132 |
133 | 多个应用程序可以注册到同一个select
134 |
135 | 2. select阻塞,等待数据
136 | 3. 内核准备好了某个应用程序A的数据,select返回,通知应用程序
137 | 4. 应用程序通过系统调用`recv`或`recvfrom`尝试接收数据
138 | 5. 内核将数据从内核空间复制到用户空间(期间应用程序阻塞)
139 | 6. 数据复制完成,应用程序开始处理数据
140 |
141 |
142 |
143 | > 多路复用IO模型首先利用到了信号驱动的优势(select需要通知应用程序数据准备好了,所以也算是一种信号驱动)
144 | >
145 | > 另外,多路复用让一个select服务多个应用程序,采用一对多的方式,节省了一定的资源成本。
146 | >
147 | > 但是在数据复制(内核空间 → 用户空间)阶段,依然无法避免应用程序的阻塞。
148 | >
149 | > 为了优化数据复制时阻塞的问题,引入了下面的异步IO模型。
150 |
151 |
152 |
153 |
154 |
155 | ## 异步IO模型
156 |
157 | ### 基本含义
158 |
159 | 和[IO(中)——深入理解JAVA层面的IO](https://mp.weixin.qq.com/s?__biz=MzA4Mjk5OTA0OQ==&mid=2247483980&idx=1&sn=1f7ae34cf2dc93b919b68dc5c90be443&chksm=9ffc602aa88be93c21fd2c298950e3c080d6a5384af906fd2f654209e61e9cee0a6a7667fce2&token=1958345249&lang=zh_CN#rd)中提到的AIO含义相似。
160 |
161 | 在异步IO模型下,应用程序永远不会阻塞,发起IO请求之后就可以做自己的事情,直到数据准备且复制完成,再去处理数据。
162 |
163 |
164 |
165 | ### 操作流程
166 |
167 | 
168 |
169 | 1. 用户进程发起aio_read,开始做其他事情
170 |
171 | 2. 内核准备数据
172 | 3. 内核把数据从内核空间复制到用户空间
173 | 4. 内核通知用户进程数据已准备完毕
174 | 5. 用户进程开始处理数据
175 |
176 |
177 |
178 | > 多路复用IO模型首先利用到了信号驱动的优势(select需要通知应用程序数据准备好了,所以也算是一种信号驱动)
179 | >
180 | > 异步IO现在使用已愈趋广泛,但由于一些底层技术及框架的局限性,在很多应用领域还未能彻底替代多路复用IO。
181 |
182 |
183 |
184 |
185 |
186 | ## 五种IO模型的比较
187 |
188 | 最后我们来用一张图和一个表格来比较一下五种IO模型。
189 |
190 | 图源于网络,表格由我自行整理。
191 |
192 | 
193 |
194 | | IO模型 | 数据准备阶段 | 数据复制阶段 | 使用场景 |
195 | | ---------- | ------------ | ------------ | ------------------------------------------------------------ |
196 | | 阻塞IO | 阻塞 | 阻塞 | 连接少,IO任务简单的场景 |
197 | | 非阻塞IO | 非阻塞 | 阻塞 | 和阻塞IO差不多,比阻塞IO的能力强一些 |
198 | | 信号驱动IO | 非阻塞 | 阻塞 | 它性能没有复用IO和异步IO强,信号使用起来又复杂,一般不太使用 |
199 | | 多路复用IO | 非阻塞 | 阻塞 | 连接多,IO任务简单的场景 |
200 | | 异步IO | 非阻塞 | 非阻塞 | 连接多,IO任务复杂的场景 |
201 |
202 |
--------------------------------------------------------------------------------
/docs/JVM/JMM Java内存模型.md:
--------------------------------------------------------------------------------
1 | JMM(JAVA内存模型)是JVM基础知识中最容易被忽略的部分,但它对于我们加深对JVM总体的理解起到举足轻重的作用,同时也是大厂面试的一个重要考点。
2 |
3 |
4 |
5 | **目录**
6 |
7 | - [基本概念](#基本概念)
8 | - [性质](#性质)
9 | - [目的](#目的)
10 | - [内容](#内容)
11 | - [规范变量访问](#规范变量访问)
12 | - [规范原子性](#规范原子性)
13 | - [规范可见性](#规范可见性)
14 | - [规范有序性](#规范有序性)
15 | - [注意事项](#注意事项)
16 | - [面试中如何回答](#面试中如何回答)
17 |
18 |
19 |
20 |
21 |
22 | ## 基本概念
23 |
24 | JMM:JAVA Memory Model 也即**JAVA内存模型**
25 |
26 | 下面分四个方面来陈述:
27 |
28 | - 性质:JMM本质上是一个什么东西。
29 | - 目的:JMM之所以存在,是为了解决什么问题?
30 | - 内容:JMM为了实现它的目的,要做些什么?
31 | - 实现:利用什么技术真正实现它的目的?
32 |
33 |
34 |
35 |
36 |
37 | ### 性质
38 |
39 | JMM是一种**规范**。
40 |
41 | 就好像HTTP是一种**交互协议**,[JAVA运行时数据区](https://mp.weixin.qq.com/s?__biz=MzA4Mjk5OTA0OQ==&mid=2247483776&idx=1&sn=25fbf98bb6dcda4e924675dc1e64997e&chksm=9ffc63e6a88beaf053d6036e5c4fff87af41df7d5a6e35329e115f9123b9b2d5c4363068f4c2&token=1776113537&lang=zh_CN#rd)是一种**内存结构**一样,JMM是对JAVA虚拟机作出**规范**,使它必须符合某些要求以满足某些目的。
42 |
43 |
44 |
45 |
46 |
47 | ### 目的
48 |
49 | 借助JMM规范,我们希望能够做到:
50 |
51 | - 屏蔽硬件和操作系统的访问差异,让Java程序在各种平台下都能达到一致的内存访问效果;
52 | - 解决多线程通过共享内存进行通信时,存在的原子性、可见性、有序性问题。
53 |
54 |
55 |
56 |
57 |
58 | ### 内容
59 |
60 | #### 规范变量访问
61 |
62 | JMM的首要目标是定义程序中各个变量的访问规则,即在虚拟机中**将变量存储到内存**和**从内存中取出变量**这样的底层细节。
63 |
64 |
65 |
68 |
69 | 线程、主内存、工作内存三者的交互关系
73 |
74 |
75 | - JMM规定了所有的变量都存储在主内存(Main Memory)中
76 | - 每个线程还有自己的工作内存(Work Memory),其中保存被该线程使用到的变量的主内存副本拷贝
77 | - 线程对变量的所有操作(读取、赋值等)都必须在工作内存中进行,不能直接读写主内存
78 | - 线程间变量值的传递需要通过主内存,不能访问其他线程的工作内存
79 |
80 |
81 |
82 | #### 规范原子性
83 |
84 | JMM要求:
85 |
86 | 除了64位的数据类型(long和double),其他基本数据类型的访问读写都是必须具备原子性的。
87 |
88 | 不过现在的商用虚拟机都会把long和double数据类型也实现为原子操作。
89 |
90 | 另外,为了能够实现更大范围的原子性保证,JMM还提供了lock和unlock两个原子操作来满足这种需求,与它们相对应的更高层次的字节码也就是`monitorenter`和`moniterexit`,也就是synchronized的底层实现方式。还不是很了解synchronized?传送门:[Synchronizd关键字](https://mp.weixin.qq.com/s?__biz=MzA4Mjk5OTA0OQ==&mid=2247483769&idx=1&sn=bdb296488a0b2b30ae98287ecd7151cf&chksm=9ffc631fa88bea09aed92f62d1c2a8b6d469ec918586fa4e011b72c304bcb8ae5541465de9d3&token=1776113537&lang=zh_CN#rd)
91 |
92 |
93 |
94 | #### 规范可见性
95 |
96 | 可见性是指**当一个线程修改了共享变量的值,其他线程能够立即得知这个修改**。
97 |
98 | 为了规范可见性,JMM引入了volatile关键字。volatile保证了:
99 |
100 | 1. 被volatile修饰的变量在被修改后立即同步到主内存
101 | 2. 被volatile修饰的变量在每次被使用之前都从主内存刷新
102 |
103 | 因此,可以使用volatile来保证多线程操作时变量的可见性。
104 |
105 | 而具体如何保证上面两条,**内存屏障**也起到至关重要的作用,篇幅原因这里就不详细展开了。如果想对volatile有更进一步了解,请移步[Volatile关键字](https://mp.weixin.qq.com/s?__biz=MzA4Mjk5OTA0OQ==&mid=2247483765&idx=1&sn=cf0c1b74985e2389719d6ae1f51f4258&chksm=9ffc6313a88bea05f7c77c45247a5a1ce251b77b13c71c0068a4b30cc2effee2e80e21e59988&token=1776113537&lang=zh_CN#rd)
106 |
107 |
108 |
109 | 除了volatile以外,还有两个关键字可以实现可见性:
110 |
111 | - synchronized
112 |
113 | 同步块的可见性是由“对一个变量执行unlock操作之前,必须先把此变量同步回主内存中(执行store、write操作)”这条规则获得的
114 |
115 | - final
116 |
117 | 被final修饰的字段在构造器中一旦初始化完成,并且构造器没有把“this”的引用传递出去(**this引用逃逸**是一件很危险的事情,其他线程有可能通过这个引用访问到“初始化了一半”的对象),那在其他线程中就能看见final修饰的值。
118 |
119 |
120 |
121 | #### 规范有序性
122 |
123 | 规范程序的有序性,也即让程序指令能够按照想要的顺序来执行。
124 |
125 | 这里的程序指令并不是一行行代码,而是应该理解为一个个原子的操作指令,它是比一行行代码更细化的粒度。
126 |
127 | JMM中利用三点来规范有序性:
128 |
129 | 1. volatile关键字防止指令重排
130 |
131 | volatile是通过**内存屏障**来防止指令重排的,如果想对volatile有更进一步了解,请移步[Volatile关键字](https://mp.weixin.qq.com/s?__biz=MzA4Mjk5OTA0OQ==&mid=2247483765&idx=1&sn=cf0c1b74985e2389719d6ae1f51f4258&chksm=9ffc6313a88bea05f7c77c45247a5a1ce251b77b13c71c0068a4b30cc2effee2e80e21e59988&token=1776113537&lang=zh_CN#rd)
132 |
133 | 2. as-if-serial语义
134 |
135 | 含义是:“在单线程下,无论指令怎样排序,最终结果都不能被改变。”
136 |
137 | 有了as-if-serial语义,才使得synchronized也能够保证有序性。
138 |
139 | 3. happens-before"先行发生"原则
140 |
141 | “先行发生”原则定义两项操作之间的偏序关系。包括了“程序次序规则”、“线程启动规则”、“传递性”等等。具体如下(简单了解即可,无需记忆):
142 |
143 | > 程序次序规则:同一个线程内,按照代码出现的顺序,前面的代码先行于后面的代码,准确的说是控制流顺序,因为要考虑到分支和循环结构。
144 | >
145 | >
146 | >
147 | > 管程锁定规则:一个unlock操作先行发生于后面(时间上)对同一个锁的lock操作。
148 | >
149 | >
150 | >
151 | > volatile变量规则:对一个volatile变量的写操作先行发生于后面(时间上)对这个变量的读操作。
152 | >
153 | >
154 | >
155 | > 线程启动规则:Thread的start( )方法先行发生于这个线程的每一个操作。
156 | >
157 | >
158 | >
159 | > 线程终止规则:线程的所有操作都先行于此线程的终止检测。可以通过Thread.join( )方法结束、Thread.isAlive( )的返回值等手段检测线程的终止。
160 | >
161 | >
162 | >
163 | > 线程中断规则:对线程interrupt( )方法的调用先行发生于被中断线程的代码检测到中断事件的发生,可以通过Thread.interrupt( )方法检测线程是否中断
164 | >
165 | >
166 | >
167 | > 对象终结规则:一个对象的初始化完成先行于发生它的finalize()方法的开始。
168 | >
169 | >
170 | >
171 | > 传递性:如果操作A先行于操作B,操作B先行于操作C,那么操作A先行于操作C。
172 |
173 |
174 |
175 |
176 |
177 | ## 注意事项
178 |
179 | 经常有人把JAVA内存模型和运行时数据区搞混,也就是下面两张图:
180 |
181 |
182 |
185 |
186 | 线程、主内存、工作内存三者的交互关系
190 |
191 |
192 |
193 |
194 |
195 |
198 |
199 | Java运行时数据区
203 |
204 |
205 | 请千万不要再搞混了。
206 |
207 |
208 |
209 |
210 |
211 | ## 面试中如何回答
212 |
213 | 面试官:**JMM了解吗?简单讲讲?**
214 |
215 |
216 |
217 | 答:
218 |
219 | JMM,JAVA内存模型,是一种用来屏蔽各种硬件和操作系统的内存访问差异的规范,目的是解决多线程通过主内存通信时,存在的原子性、可见性、有序性问题。
220 |
221 | JMM首先规范了变量的访问。它要求所有变量都要存在主内存中,每个线程在自己工作内存中操作变量,然后同步回主内存;
222 |
223 | 其次JMM利用内存间原子性的交互操作规范了原子性,例如lock、unlock、read、store等等。通过这些操作,JMM保证了基本数据类型操作的原子性,并支持了synchronized;
224 |
225 | JMM还利用volatile实现了可见性,它要求被volatile修饰的变量在使用时与主存及时同步;
226 |
227 | 最后JMM还保证了有序性,这是由volatile关键字的内存屏障、as-if-serial语义,以及happens-before“先行发生”原则一同支持的。
228 |
229 | > 以上是概述性回答,然后根据面试官提的问题,可以再详细展开。例如:“面试官:‘volatile怎么保证可见性的?'”就可以再讲讲主内存与工作内存的及时性同步,还有内存屏障等等。
230 |
231 |
--------------------------------------------------------------------------------
/docs/Spring/Spring IOC.md:
--------------------------------------------------------------------------------
1 | 框架是JAVA面试中比较重要的一个部分,而Spring是面试最爱问的框架之一。Spring中最重要的就是IOC和AOP这两大概念,本篇中对于IOC相关知识及面试题作一个总结。
2 |
3 |
4 |
5 | **目录**
6 |
7 | - [基本概念](#基本概念)
8 | - [性质](#性质)
9 | - [目的](#目的)
10 | - [内容](#内容)
11 | - [没有引入IOC的软件系统](#没有引入IOC的软件系统)
12 | - [引入IOC的软件系统](#引入IOC的软件系统)
13 | - [引入IOC的效果](#引入IOC的效果)
14 | - [常见面试题](#常见面试题)
15 | - [Bean作用域](#(1)Bean作用域)
16 | - [Bean生命周期](#(2)Bean生命周期)
17 | - [BeanFactory和ApplicationContext的区别](#(3)BeanFactory和ApplicationContext的区别)
18 | - [核心源码梳理](#核心源码梳理)
19 | - [Bean解析](#(1)Bean解析)
20 | - [Bean注册](#(2)Bean注册)
21 | - [Bean加载](#(3)Bean加载)
22 |
23 |
24 |
25 |
26 |
27 | ## IOC基本概念
28 |
29 | IOC:Inverse of Control 也即**控制反转**
30 |
31 | 下面分四个方面来陈述:
32 |
33 | - 性质:IOC本质上是一个什么东西。
34 | - 目的:IOC之所以存在,是为了解决什么问题?
35 | - 内容:IOC为了实现它的目的,要做些什么?
36 |
37 |
38 |
39 |
40 |
41 | ### 性质
42 |
43 | IOC是一种**设计思想**。
44 |
45 | 它不是一种具体的技术,虽然我们经常把IOC和Spring联系在一起,但其实它也完全可以体现在其他的框架和语言中。
46 |
47 |
48 |
49 |
50 |
51 | ### 目的
52 |
53 | 借助IOC设计思想,我们希望能够做到**互相依赖的对象之间的解耦**。
54 |
55 |
56 |
57 |
58 |
59 | ### 内容
60 |
61 | #### 没有引入IOC的软件系统
62 |
63 | 软件系统内部肯定有若干个对象存在,其中部分与部分对象之间存在着互相调用或依赖的关系,导致对象之间耦合度较高,如下图:
64 |
65 | 
66 |
67 | 我们可以把对象比喻成齿轮。如果几个耦合的对象中有一个对象不动了(无法正常工作),那么其他的对象也无法正常工作。导致“牵一发而动全身”的情况。
68 |
69 |
70 |
71 | #### 引入IOC的软件系统
72 |
73 | 在引入IOC容器后,我们再来看一下这个软件系统:
74 |
75 | 
76 |
77 | 齿轮之间的转动交给了“第三方”——也就是IOC容器。
78 |
79 | 由IOC容器来对各个对象进行统一的管理,当某个对象B需要对象A来帮助它完成什么功能时,就由IOC容器来把容器管理的A“借给”(专业术语叫“注入”)B来使用。
80 |
81 |
82 |
83 | #### 引入IOC的效果
84 |
85 | 对于这一点,我们不妨来看一下去掉IOC容器后的这张图:
86 |
87 | 
88 |
89 | 可以发现对象与对象之间不再紧密贴合。
90 |
91 | 这样的话,在我们编码实现A的时候,就无需考虑B、C和D了。
92 |
93 | 也即达到了我们想要的“**互相依赖的对象之间的解耦**”。
94 |
95 |
96 |
97 |
98 |
99 | ## 常见面试题
100 |
101 | 对于Spring IOC容器,最核心的两点就是:
102 |
103 | - 容器本身
104 | - 容器里的元素,或者说对象(bean)
105 |
106 | 围绕这两点,我们可以整理出下面三个非常重要的面试题。
107 |
108 |
109 |
110 | ### (1)Bean作用域
111 |
112 | Spring容器中的bean可以分为5个作用域:
113 |
114 | - singleton:默认,容器中只有一个bean的实例,由BeanFactory自身来维护。
115 |
116 | - prototype:为每一个bean请求提供一个实例。
117 |
118 | - request:为每一个网络请求创建一个实例,在请求完成以后,bean会失效并被垃圾回收器回收。
119 |
120 | - session:与request范围类似,确保每个session中有一个bean的实例,在session过期后,bean会随之失效。
121 |
122 | - global-session:全局作用域,global-session和Portlet应用相关。当你的应用部署在Portlet容器中工作时,它包含很多portlet。如果你想要声明让所有的portlet共用全局的存储变量的话,那么这全局变量需要存储在global-session中。全局作用域与Servlet中的session作用域效果相同。
123 |
124 |
125 |
126 | ### (2)Bean生命周期
127 |
128 | bean从出生到消亡,经历了哪些过程?
129 |
130 | 1. 实例化Bean:
131 |
132 | 对于BeanFactory容器,当客户向容器请求一个尚未初始化的bean时,或初始化bean的时候需要注入另一个尚未初始化的依赖时,容器就会调用createBean进行实例化。对于ApplicationContext容器,当容器启动结束后,通过获取BeanDefinition对象中的信息,实例化所有的bean。
133 |
134 | 2. 设置对象属性(依赖注入):
135 |
136 | 实例化后的对象被封装在BeanWrapper对象中,紧接着,Spring根据BeanDefinition中的信息 以及 通过BeanWrapper提供的设置属性的接口完成依赖注入。
137 |
138 | 3. 处理Aware接口:
139 |
140 | 接着,Spring会检测该对象是否实现了xxxAware接口,并将相关的xxxAware实例注入给Bean:
141 | 1. 如果这个Bean已经实现了BeanNameAware接口,会调用它实现的setBeanName(String beanId)方法,此处传递的就是Spring配置文件中Bean的id值;
142 | 2. 如果这个Bean已经实现了BeanFactoryAware接口,会调用它实现的setBeanFactory()方法,传递的是Spring工厂自身。
143 | 3. 如果这个Bean已经实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文;
144 |
145 | 4. BeanPostProcessor:
146 |
147 | 如果想对Bean进行一些自定义的处理,那么可以让Bean实现了BeanPostProcessor接口,那将会调用postProcessBeforeInitialization(Object obj, String s)方法。
148 |
149 | 5. InitializingBean 与 init-method:
150 |
151 | 如果Bean在Spring配置文件中配置了 init-method 属性,则会自动调用其配置的初始化方法。
152 |
153 | 6. BeanPostProcessor
154 |
155 | 如果这个Bean实现了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法;由于这个方法是在Bean初始化结束时调用的,所以可以被应用于内存或缓存技术;
156 |
157 | > 以上几个步骤完成后,Bean就已经被正确创建了,之后就可以使用这个Bean了。
158 |
159 | 7. DisposableBean:
160 |
161 | 当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用其实现的destroy()方法;
162 |
163 | 8. destroy-method:
164 |
165 | 最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法。
166 |
167 |
168 |
169 | ### (3)BeanFactory和ApplicationContext的区别
170 |
171 | BeanFactory和ApplicationContext是Spring的两大核心接口,都可以当做Spring的容器。其中ApplicationContext是BeanFactory的子接口。
172 |
173 | - BeanFactory:是Spring里面最底层的接口,包含了各种Bean的定义,读取bean配置文档,管理bean的加载、实例化,控制bean的生命周期,维护bean之间的依赖关系。ApplicationContext接口作为BeanFactory的派生,除了提供BeanFactory所具有的功能外,还提供了更完整的框架功能:
174 | - 继承MessageSource,因此支持国际化。
175 | - 统一的资源文件访问方式。
176 | - 提供在监听器中注册bean的事件。
177 | - 同时加载多个配置文件。
178 | - 载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层。
179 |
180 |
181 |
182 | 其他的一些不同处:
183 |
184 | 先列个表格,然后下面详细说明。
185 |
186 | | | BeanFactory | ApplicationContext |
187 | | ------------------------------------ | ----------- | -------------------- |
188 | | **加载方式** | 惰性加载 | 预先加载 |
189 | | **启动速度** | 较快 | 较慢(加载Bean过多) |
190 | | **创建方式** | 编程式 | 编程式或声明式 |
191 | | **Bean(Factory)PostProcessor扩展点** | 手动注册 | 自动注册 |
192 |
193 |
194 |
195 | - 加载方式
196 |
197 | BeanFactroy采用的是惰性加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化。这样,我们就不能发现一些存在的Spring的配置问题。如果Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次使用调用getBean方法才会抛出异常。
198 |
199 | ApplicationContext是在容器启动时,一次性创建了所有的Bean。这样,在容器启动时,我们就可以发现Spring中存在的配置错误,这样有利于检查所依赖属性是否注入。 ApplicationContext启动后预载入所有的单实例Bean,通过预载入单实例bean ,确保当你需要的时候,你就不用等待,因为它们已经创建好了。
200 |
201 | - 启动速度
202 |
203 | 相对于基本的BeanFactory,ApplicationContext 唯一的不足是占用内存空间。当应用程序配置Bean较多时,程序启动较慢。
204 |
205 | - 创建方式
206 |
207 | BeanFactory通常以编程的方式被创建,ApplicationContext还能以声明的方式创建,如使用ContextLoader。
208 |
209 | - 扩展点的注册
210 |
211 | BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册。
212 |
213 |
214 |
215 |
216 |
217 | ## 核心源码梳理
218 |
219 | 在Spring IOC方面,最核心的源码也就是这三步:
220 |
221 | 1. bean解析
222 | 2. bean注册
223 | 3. bean加载
224 |
225 | 限于篇幅,这里仅对三段源码的核心逻辑做一个整理,这也是在我看来比较有用,但网上又很少有人去写的。
226 |
227 | 而至于真正要去深读源码的话,建议在网上找一些大牛的博客,或者阅读《Spring源码深度解析》这本书。
228 |
229 |
230 |
231 | ### (1)Bean解析
232 |
233 | 主要流程:
234 |
235 | 1. Spring载入Bean的XML配置文件
236 |
237 | 2. 把XML文件解析为Resource对象
238 |
239 | Resource接口抽象了所有Spring内部使用到的底层资源,例如File、URL、Classpath等等。
240 |
241 | 3. 加载XML得到对应的Document
242 |
243 | 4. 根据Document注册Bean
244 |
245 | XmlBeanDefinitionReader的registerBeanDefinitions方法 → DefaultBeanDefinitionDocumentReader的parseBeanDefinitions方法,其间有各种标签解析。有兴趣的读者可以去看看这一段的源码,是解析阶段里相对比较重要的源码
246 |
247 | 5. 解析并注册
248 |
249 | 解析完成的bean以BeanDefinition的形式注册到BeanDefinitionRegistry(就是我们的IOC容器)中,以map形式保存。
250 |
251 |
252 |
253 | 其中**BeanDefinition**是一个关键的点,需要注意一下:
254 |
255 | BeanDefinition是一个接口,在Spring中存在三种实现:RootBeanDefinition、ChildBeanDefinition以及GenericBeanDefinition。其中GenericBeanDefinition是xml中读到的bean信息第一步存储的形式,之后会转为RootBeanDefinition。
256 |
257 | - 父bean用RootBeanDefinition表示,
258 |
259 | - 子bean用ChildBeanDefinition表示,
260 |
261 | - 没有父bean的bean用RootBeanDefinition表示。
262 |
263 | - GenericBeanDefinition是一站式服务类。
264 |
265 | 什么叫一站式服务类?可以理解为就是过渡用的类,最后还是要转为RootBeanDefinition和ChildBeanDefinition的。
266 |
267 |
268 |
269 | ### (2)Bean注册
270 |
271 | 将beanDefinition直接放到map中,使用beanName作为key。
272 |
273 | 不过在真正注册之前,要做一些准备工作(了解即可):
274 |
275 | 1. 校验AbstractBeanDefinition的methodOverrides属性
276 | 2. 对beanName已经注册的情况处理(如果不允许beanName覆盖就抛出异常)
277 | 3. 加入map缓存
278 | 4. 清除解析之前留下的对应beanName的缓存
279 |
280 |
281 |
282 | ### (3)Bean加载
283 |
284 | 在AbstractBeanFactory的`doGetBean(final String name, @Nullable final Class requiredType,@Nullable final Object[] args, boolean typeCheckOnly)`中加载
285 |
286 | 1. 转换对应beanName为真实的beanName(转换别名为真正的beanName,如果是FactoryBean就去除开头的&符号)
287 |
288 | - 之所以如果是FactoryBean就要做处理,是因为bean的class属性实现类是FactoryBean时,通过getBean返回的不是FactoryBean本身,而是FactoryBean的getObject方法返回的对象
289 |
290 | - FactoryBean是为了隐藏实例化一些复杂bean的细节,给上层应用带来便利。FactoryBean使用了工厂模式
291 |
292 | 2. 尝试从缓存中加载单例
293 |
294 | 1. 进入方法`getSingleton(String beanName, boolean allowEarlyRefe rence)`且allowEarlyReference为true
295 | 1. 优先在**singletonObjects**找
296 | 2. 没有就在**earlySingletonObjects**找
297 | 3. 再没有就取出对应的**singletonFactories**.get(beanName).getObject(),并存到**earlySingletonObjects**里
298 |
299 | 2. 如果缓存中能找到
300 | 1. bean的实例化(对缓存中取出的原始状态bean进行加工)
301 | 3. 如果缓存中找不到
302 | 1. prototype模式的依赖检查(有循环依赖就抛出异常,只有单例情况才会尝试解决循环依赖)
303 | 2. 如果当前beanFactory中没有相应beanName,就尝试从parentFactory里检测、取出beanName对应bean
304 | 3. 将GenericBeanDefinition转换为RootBeanDefinition(这是因为之后对于bean的所有后续处理都是针对RootBeanDefinition进行的)
305 | 4. 寻找依赖,如果存在依赖就递归初始化依赖的bean
306 | 5. 针对不同的scope进行bean创建
307 | 6. 类型转换(例如返回的bean是个String,但requiredType传Integer)
308 |
309 |
310 |
311 |
312 | 其中2.1需要利用三级缓存解决循环依赖问题:
313 |
314 | (在DefaultSingletonBeanRegistry的`getSingleton(String beanName, boolean allowEarlyReference)`方法中)
315 |
316 | 涉及到的重点缓存map:
317 |
318 | **singletonObjects**:存放初始化好的bean(beanName → bean实例)
319 |
320 | **earlySingletonObjects**:存放刚实例化好的,但是还未配置属性和初始化的bean(提前曝光的单例对象)
321 |
322 | **singletonFactories**:存放beanName和创建bean的工厂之间的关系(beanName → ObjectFactory)
323 |
324 | 举例:
325 |
326 | 例如循环依赖A → B → A
327 |
328 | 1. 尝试getSingleton("A",true)
329 |
330 | singletonObjects里找不到,并且A并不在创建状态(`isSingletonCurrentlyInCreation(beanName)`为false),直接返回空(也就是缓存取不到)
331 |
332 | 缓存取不到就开始直接尝试加载,加载时要递归加载依赖的bean,所以接下来尝试加载B
333 |
334 | 2. 尝试getSingleton("B",true)
335 |
336 | singletonObjects里找不到,并且B并不在创建状态,直接返回空
337 |
338 | 开始尝试加载B依赖的bean,也就是A
339 |
340 | 3. 尝试getSingleton("A",true)
341 |
342 | singletonObjects里找不到,并且A在创建状态,所以去earlySingletonObjects里找
343 |
344 | 发现earlySingletonObjects里也没有,就从singletonFactories里拿,提前曝光到earlySingletonObjects里
345 |
346 | A成为一个还未初始化完全,但是可以被依赖的bean
347 |
348 | 4. B成功初始化
349 | 5. A成功初始化
350 |
351 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | > 本仓库适用对象:
2 | >
3 | > 1. 想要实践Java后台开发的新手
4 | > 2. 想要拿offer到手软的应届生、低工龄社会人
5 |
6 |
7 |
8 | 更多原创内容和干货分享:
9 |
10 | 1. [公众号—Jyannis](#公众号) : 每日面试题分享 + 第一手热乎乎面经及面试故事
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | ## 目录
25 |
26 | - [我是零基础,怎么上手Java后台开发(SpringBoot)?](https://zhuanlan.zhihu.com/p/97958284)
27 | - [我刚学明白SpringBoot,怎么开展学习微服务?](https://github.com/jyannis/SpringCloud-Alibaba-Learning)
28 | - [Java技术栈学习书籍推荐](https://mp.weixin.qq.com/s?__biz=MzA4Mjk5OTA0OQ==&mid=2247483774&idx=1&sn=76f898602aa9af5913f4c9ea82fc0d20&chksm=9ffc6318a88bea0ed50d39255bc27366391c62d84b3bbda79772ba11cb740070e91dd7b50bec&token=1776113537&lang=zh_CN#rd)
29 | - Java
30 | - 常见Java基础面试题
31 | - 容器(集合框架)
32 | - JVM
33 | - [运行时数据区](https://mp.weixin.qq.com/s?__biz=MzA4Mjk5OTA0OQ==&mid=2247483776&idx=1&sn=25fbf98bb6dcda4e924675dc1e64997e&chksm=9ffc63e6a88beaf053d6036e5c4fff87af41df7d5a6e35329e115f9123b9b2d5c4363068f4c2&token=1776113537&lang=zh_CN#rd)
34 | - [垃圾回收](https://mp.weixin.qq.com/s?__biz=MzA4Mjk5OTA0OQ==&mid=2247483779&idx=1&sn=6ae9053df60475e7033ed6f17615b7e4&chksm=9ffc63e5a88beaf3f717c04274c087f7b38b8cdcb65ac20d165ee301a3c1dc28d10260b3cca1&token=1776113537&lang=zh_CN#rd)
35 | - [Java四种引用类型](https://mp.weixin.qq.com/s?__biz=MzA4Mjk5OTA0OQ==&mid=2247483783&idx=1&sn=c04e4ff81c62e1048f56931fb60eb845&chksm=9ffc63e1a88beaf763aa90e484fae5b9506c5c68224b657ab46d29aa51316f72ee71ba823ddf&token=1776113537&lang=zh_CN#rd)
36 | - [类加载机制](https://mp.weixin.qq.com/s?__biz=MzA4Mjk5OTA0OQ==&mid=2247483777&idx=1&sn=f330cb56e1bf6e0863792c6160299416&chksm=9ffc63e7a88beaf16eeb8a05a6081dfa0baf2306779d2761c480e65262eee776bb2f8c88de9d&token=1776113537&lang=zh_CN#rd)
37 | - [调优命令行工具](https://mp.weixin.qq.com/s?__biz=MzA4Mjk5OTA0OQ==&mid=2247483719&idx=1&sn=fc747d2e5b9b1928f312f1cb9a643f3b&chksm=9ffc6321a88bea37aacff8850cd60b9b4e7b1540850daf97dfe853d1d1a3b07d30fe6d3ed250&token=1776113537&lang=zh_CN#rd)
38 | - [Volatile关键字](https://mp.weixin.qq.com/s?__biz=MzA4Mjk5OTA0OQ==&mid=2247483765&idx=1&sn=cf0c1b74985e2389719d6ae1f51f4258&chksm=9ffc6313a88bea05f7c77c45247a5a1ce251b77b13c71c0068a4b30cc2effee2e80e21e59988&token=1776113537&lang=zh_CN#rd)
39 | - [JMM](https://github.com/jyannis/JavaLearning/blob/master/docs/JVM/JMM%20Java%E5%86%85%E5%AD%98%E6%A8%A1%E5%9E%8B.md)
40 | - [JVM问题排查实战](#JVM问题排查实战)
41 | - 并发
42 | - Java与线程的基础知识
43 | - [Synchornized关键字](https://mp.weixin.qq.com/s?__biz=MzA4Mjk5OTA0OQ==&mid=2247483769&idx=1&sn=bdb296488a0b2b30ae98287ecd7151cf&chksm=9ffc631fa88bea09aed92f62d1c2a8b6d469ec918586fa4e011b72c304bcb8ae5541465de9d3&token=1776113537&lang=zh_CN#rd)
44 | - 锁优化
45 | - [同步组件](https://mp.weixin.qq.com/s?__biz=MzA4Mjk5OTA0OQ==&mid=2247483773&idx=1&sn=53c49298fae09a9e8bc089bf2cc0f046&chksm=9ffc631ba88bea0de0e26906c1bfb2e7234aafb8044a8d3d798c01457be77a57d57c0858c2cb&token=1776113537&lang=zh_CN#rd)
46 | - [线程池](https://mp.weixin.qq.com/s?__biz=MzA4Mjk5OTA0OQ==&mid=2247483763&idx=1&sn=73501f177d4882a23401e614b7a06b5d&chksm=9ffc6315a88bea030635a6fc138fe4d41b10a2801c04f7eefdbe642b0317d5a9de98e627edef&token=1776113537&lang=zh_CN#rd)
47 | - 并发容器
48 | - [ThreadLocal](https://mp.weixin.qq.com/s?__biz=MzA4Mjk5OTA0OQ==&mid=2247483891&idx=1&sn=7a6f3790a5fccd9cd378141215941a18&chksm=9ffc6395a88bea831a9b7c03fa50abefba84574f16f2c7c7186ce1e8097bf8f199847d041943&token=1533871073&lang=zh_CN#rd)
49 | - MySQL
50 | - [索引](https://mp.weixin.qq.com/s?__biz=MzA4Mjk5OTA0OQ==&mid=2247483844&idx=1&sn=d49ea52feb5efefc6f62fdb27bb2bc39&chksm=9ffc63a2a88beab497fc893b09657dd76bfe8135275cd01bca1e9ca5589c3b99de5f28a9f2ba&token=1776113537&lang=zh_CN#rd)
51 | - [锁](https://mp.weixin.qq.com/s?__biz=MzA4Mjk5OTA0OQ==&mid=2247483775&idx=1&sn=a9f52d630f191aa1afcf71e38e595800&chksm=9ffc6319a88bea0f8c37b39d1b9aeabb03b7f403e832729c4172115d30db91325bcf3ce57535&token=1776113537&lang=zh_CN#rd)
52 | - [Explain执行计划分析](https://mp.weixin.qq.com/s?__biz=MzA4Mjk5OTA0OQ==&mid=2247483863&idx=1&sn=9a25e58d37ee017688805fee46f59bb1&chksm=9ffc63b1a88beaa79269295884f3f4a275edd3879c3131784bd32255fe21e022e9470f986b9d&token=1776113537&lang=zh_CN#rd)
53 | - 分区
54 | - [InnoDB存储引擎体系结构](https://mp.weixin.qq.com/s?__biz=MzA4Mjk5OTA0OQ==&mid=2247483864&idx=1&sn=04ffd18b14d868acc72865bec6dd5e24&chksm=9ffc63bea88beaa8780b77edf6fed1bb72bc12c06d8919ffc5a6223f14dcd3e48665b59173f8&token=1776113537&lang=zh_CN#rd)
55 | - 文件
56 | - 查询处理流程
57 | - Redis
58 | - [概述](https://github.com/jyannis/JavaLearning/blob/master/docs/redis/概述.md)
59 | - [数据结构](https://github.com/jyannis/JavaLearning/blob/master/docs/redis/数据结构.md)
60 | - [持久化](https://github.com/jyannis/JavaLearning/blob/master/docs/redis/持久化.md)
61 | - [常见缓存问题及其解决](https://github.com/jyannis/JavaLearning/blob/master/docs/redis/常见缓存问题及其解决.md)
62 | - [常用命令](https://github.com/jyannis/JavaLearning/blob/master/docs/redis/常用命令.md)
63 | - Spring
64 | - [IOC](https://github.com/jyannis/JavaLearning/blob/master/docs/Spring/Spring%20IOC.md)
65 | - [AOP](https://github.com/jyannis/JavaLearning/blob/master/docs/Spring/Spring%20AOP.md)
66 | - 事务
67 | - [网络](https://mp.weixin.qq.com/s?__biz=MzA4Mjk5OTA0OQ==&mid=2247483809&idx=1&sn=ddf5933cf18ed6f5ba4e30a634f18da3&chksm=9ffc63c7a88bead1a8c91fa846a7ba7bacbf8faff04b6ec4d3f99909331a0a369de75d036006&token=1776113537&lang=zh_CN#rd)
68 | - 操作系统
69 | - 设计模式
70 | - [装饰者Decorator](https://github.com/jyannis/design-patterns/tree/master/Decorator)
71 | - [观察者Observer](https://github.com/jyannis/design-patterns/tree/master/Observer)
72 | - [策略Strategy](https://github.com/jyannis/design-patterns/tree/master/Strategy)
73 | - 算法
74 | - IO
75 | - [一文读懂BIO、NIO、AIO](https://github.com/jyannis/JavaLearning/blob/master/docs/IO/%E4%B8%80%E6%96%87%E8%AF%BB%E6%87%82BIO%E3%80%81NIO%E3%80%81AIO.md)
76 | - [各种面经与面试故事](#面经与面试故事)
77 | - 面试技巧
78 | - [如何进行知识积累](https://github.com/jyannis/JavaLearning/blob/master/docs/tech-life/今天我收到了蚂蚁金服实习offer.md#如何进行知识积累)
79 | - [如何把握实践与理论的天平](https://github.com/jyannis/JavaLearning/blob/master/docs/tech-life/今天我收到了蚂蚁金服实习offer.md#如何把握实践与理论的天平)
80 | - [我应该如何整理笔记](https://github.com/jyannis/JavaLearning/blob/master/docs/tech-life/今天我收到了蚂蚁金服实习offer.md#我应该如何整理笔记)
81 | - [怎么复习才不会忘](https://github.com/jyannis/JavaLearning/blob/master/docs/tech-life/今天我收到了蚂蚁金服实习offer.md#怎么复习才不会忘)
82 | - [面试前](https://github.com/jyannis/JavaLearning/blob/master/docs/tech-life/今天我收到了蚂蚁金服实习offer.md#面试前)
83 | - [怎么写出让人眼前一亮的简历](https://github.com/jyannis/JavaLearning/blob/master/docs/tech-life/今天我收到了蚂蚁金服实习offer.md#怎么写出让人眼前一亮的简历)
84 | - [如何突击面试](https://github.com/jyannis/JavaLearning/blob/master/docs/tech-life/今天我收到了蚂蚁金服实习offer.md#如何突击面试)
85 | - [面试前焦虑该怎么办](https://github.com/jyannis/JavaLearning/blob/master/docs/tech-life/今天我收到了蚂蚁金服实习offer.md#面试前焦虑该怎么办)
86 | - [面试开始](https://github.com/jyannis/JavaLearning/blob/master/docs/tech-life/今天我收到了蚂蚁金服实习offer.md#面试开始)
87 | - [自我介绍到底要怎么说](https://github.com/jyannis/JavaLearning/blob/master/docs/tech-life/今天我收到了蚂蚁金服实习offer.md#自我介绍到底要怎么说)
88 | - [面试中](https://github.com/jyannis/JavaLearning/blob/master/docs/tech-life/今天我收到了蚂蚁金服实习offer.md#面试中)
89 | - [技术面试的时候应该注意些什么](https://github.com/jyannis/JavaLearning/blob/master/docs/tech-life/今天我收到了蚂蚁金服实习offer.md#技术面试的时候应该注意些什么)
90 | - [面试尬场怎么办](https://github.com/jyannis/JavaLearning/blob/master/docs/tech-life/今天我收到了蚂蚁金服实习offer.md#面试尬场怎么办)
91 | - [如何学会埋坑](https://github.com/jyannis/JavaLearning/blob/master/docs/tech-life/今天我收到了蚂蚁金服实习offer.md#如何学会埋坑)
92 | - [面试结束](https://github.com/jyannis/JavaLearning/blob/master/docs/tech-life/今天我收到了蚂蚁金服实习offer.md#面试结束)
93 | - [面试官:“你有什么想问我的吗”,该说什么?](https://github.com/jyannis/JavaLearning/blob/master/docs/tech-life/今天我收到了蚂蚁金服实习offer.md#面试官:"你有什么想问我的吗",该说什么?)
94 | - [我怎样才能知道我是否通过了](https://github.com/jyannis/JavaLearning/blob/master/docs/tech-life/今天我收到了蚂蚁金服实习offer.md#我怎样才能知道我是否通过了)
95 | - 技术人生
96 | - [今天我收到了蚂蚁金服实习offer](https://github.com/jyannis/JavaLearning/blob/master/docs/tech-life/今天我收到了蚂蚁金服实习offer.md)
97 |
98 |
99 |
100 |
101 |
102 | ## JVM问题排查实战
103 |
104 | - [发生死锁如何排查](https://blog.csdn.net/m0_46311226/article/details/104970857)
105 | - [CPU过高如何排查](https://blog.csdn.net/m0_46311226/article/details/105010210)
106 | - [发生OOM如何排查](https://blog.csdn.net/m0_46311226/article/details/104996664)
107 |
108 |
109 |
110 |
111 |
112 | ## 面经与面试故事
113 |
114 | 面经:由jyannis一个字一个字地整理答案,供读者自测与复习。
115 |
116 | - 阿里
117 | - [阿里云一面 2020-03-11](https://mp.weixin.qq.com/s?__biz=MzA4Mjk5OTA0OQ==&mid=2247483795&idx=1&sn=68b8a8607863a8b57de367bfbafd325c&chksm=9ffc63f5a88beae3bf9026fb77174edd9814906a9330149b379ca5919a2f9d22a283ecaa17c6&token=1776113537&lang=zh_CN#rd)
118 | - [淘系部门一面 2020-03-14](https://mp.weixin.qq.com/s?__biz=MzA4Mjk5OTA0OQ==&mid=2247483850&idx=1&sn=b2323dc0690791f2efb8b7287d3d3218&chksm=9ffc63aca88beabae428a6682c84b9fc8861b89aeef3ccfa97474508a3f52fff68d59bdcc698&token=1776113537&lang=zh_CN#rd)
119 | - [淘系部门一面 2020-03-15](https://mp.weixin.qq.com/s?__biz=MzA4Mjk5OTA0OQ==&mid=2247483859&idx=1&sn=03f4b1b3a66a8d6ab671ac15d837109a&chksm=9ffc63b5a88beaa3ed51cd7da9e6ae74309ebc9d89a94ee733f4e63e7cbbec1ab4f608cc3f07&token=1776113537&lang=zh_CN#rd)
120 |
121 |
122 |
123 | 面试故事:jyannis身边的同学真实的面试经历
124 |
125 | - 阿里
126 | - [淘系部门三面](https://mp.weixin.qq.com/s?__biz=MzA4Mjk5OTA0OQ==&mid=2247483816&idx=1&sn=a0f040286dc5a54f7139421e802a8705&chksm=9ffc63cea88bead8974be27ed3fdb828d9439c5626f8bbb291d4aa2270329f5dd27a33ced115&token=1776113537&lang=zh_CN#rd)
127 | - 字节跳动
128 | - [中台部门一面](https://mp.weixin.qq.com/s?__biz=MzA4Mjk5OTA0OQ==&mid=2247483835&idx=1&sn=0e1e8f8f52e98e6fd84552ff168c6d2e&chksm=9ffc63dda88beacb563e078874f87c0c79cf6922d7eb36e3282cc426bd1a60e295b6674f4972&token=1776113537&lang=zh_CN#rd)
129 | - [中台部门二面](https://mp.weixin.qq.com/s?__biz=MzA4Mjk5OTA0OQ==&mid=2247483831&idx=1&sn=68ddf105230922302e3d7b051efe7c8a&chksm=9ffc63d1a88beac7facb9f1e1880075c45bc5e8764e6951ac8cb2eb2e0f15b7183d24610bf5a&token=1776113537&lang=zh_CN#rd)
130 | - 携程
131 | - [后台日常实习](https://mp.weixin.qq.com/s?__biz=MzA4Mjk5OTA0OQ==&mid=2247483836&idx=1&sn=3f47d42abb1a9ad074acf8b22f4145ca&chksm=9ffc63daa88beacce39f45211b36de0badf00c143d7f0d125ee61f0a025facc6d8f419142799&token=1776113537&lang=zh_CN#rd)
132 |
133 |
134 |
135 |
136 |
137 | ## 说明
138 |
139 | 开源项目在于大家的参与,这才使得它得以进步。点一下watch是对我最大的支持。
140 |
141 |
142 |
143 | ### 作者介绍
144 |
145 | 我是来自华东师范大学软件工程专业的2017级本科生。
146 |
147 | 于高三毕业(2017年7月)开始学习Java语言,在今年(2020,大三)先后斩获携程(1月)、字节跳动(三月)、蚂蚁金服(四月)实习生offer,并拿到最高的双A评级(本部门P9评A + 交叉面P9评A),大概率转正。
148 |
149 | 作为一名踩过荆棘走过弯路的Java学习者,希望借助这篇开源文档对后来者提供一些帮助。
150 |
151 |
152 |
153 | ### 关于转载
154 |
155 | 如果你需要转载本仓库的一些文章到自己的博客的话,记得注明原文地址就可以了。
156 |
157 | ### 如何对该开源文档进行贡献
158 |
159 | 1. 笔记内容大多是手敲,所以难免会有笔误,你可以帮我找错别字。
160 | 2. 很多知识点我可能没有涉及到,所以你可以对其他知识点进行补充。
161 | 3. 现有的知识点难免存在不完善或者错误,所以你可以对已有知识点进行修改/补充。
162 |
163 |
164 |
165 |
166 |
167 | ### 联系我
168 |
169 | 疫情阶段,jyannis最近闲得慌,欢迎大家来打扰:
170 |
171 |
172 |
173 | 微信:
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 | 扣扣:
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 | ### 公众号
190 |
191 | 欢迎关注jyannis,不定期分享第一手笔经面经,及最热最潮的技术知识。
192 |
193 | 你的每一次点击和在看都是对我最大的支持!
194 |
195 | 
196 |
197 |
--------------------------------------------------------------------------------
/docs/redis/常用命令.md:
--------------------------------------------------------------------------------
1 | 这里是一些Redis常用命令的整理,了解即可。
2 |
3 | **目录**
4 |
5 | - [字符串String](#字符串String)
6 |
7 | - [哈希Hash](#哈希Hash)
8 |
9 | - [列表List](#列表List)
10 |
11 | - [集合Set](#集合Set)
12 |
13 | - [有序集合Zset](#有序集合Zset)
14 |
15 | - [键管理](#键管理)
16 | - [单个键管理](#单个键管理)
17 | - [键迁移](#键迁移)
18 | - [渐进式键遍历](#渐进式键遍历)
19 |
20 |
21 |
22 | ## 字符串String
23 |
24 | | **命令** | **含义** | **备注** |
25 | | ---------------------------------- | ------------------------------------------------------------ | ----------------------------------------------------- |
26 | | set(nx/xx) {key} {value} | 设置值nx:key不存在才能设置成功xx:key必须存在才能设置成功 | setnx hello redis如果hello已存在就返回0不存在就返回OK |
27 | | setex {key} {seconds} {value} | 设置会在seconds秒后过期的值 | |
28 | | get {key} | 获取值 | |
29 | | mset {key} {value} {key} {value}…… | 批量设置值 | mset a 1 b 2 c 3 |
30 | | mget {key} {key} {key}…… | 批量获取值 | mget a b c d |
31 | | exists {key} | 判断key是否存在 | exists hello |
32 | | incr {key} | 对值做自增。值不是整数则返回错误;值是整数,返回自增后结果;key不存在,按照0自增,返回1。 | |
33 | | incrby {key} {increment} | 自增指定数字 | |
34 |
35 |
36 |
37 |
38 |
39 | ## 哈希Hash
40 |
41 | | **命令** | **含义** | **备注** |
42 | | --------------------------------------------- | --------------------------- | ---------------------------------- |
43 | | hset(nx) {key} {field} {value} | 设置值 | hset user:1 name tom |
44 | | hget {key} {field} | 获取值 | hget user:1 name |
45 | | hdel {key} {field} {field} …… | 批量删除field | hdel user:1 name |
46 | | hlen {key} | 计算field个数 | hlen user:1 |
47 | | hmset {key} {field} {value} {field} {value}…… | 批量设置值 | |
48 | | hmget key {field} {field}…… | 批量获取值 | |
49 | | hexists {key} {field} | 判断field是否存在 | hexists user:1 name |
50 | | hkeys {key} | 获取所有field | hkey user:1 |
51 | | hvals {key} | 获取所有value | |
52 | | hgetall {key} | 获取所有field和所有value | 最好用hscan渐进式遍历来替代hgetall |
53 | | hincrby {key} {field} | 和incrby一样,只是针对field | |
54 |
55 |
56 |
57 |
58 |
59 | ## 列表List
60 |
61 | | **命令** | **含义** | **备注** |
62 | | ------------------------------------------- | ------------------------------------------------------------ | -------------------------------------------- |
63 | | rpush {key} {value} {value}…… | 从右边插入元素 | rpush listkey c b a |
64 | | lpush | 同上。从左边插入元素 | |
65 | | linsert {key} before\|after {pivot} {value} | 找到列表中等于pivot的元素,在其前或后插入元素value | linsert listkey before b java |
66 | | lrange {key} {start} {end} | 获取指定范围内的元素列表(双闭区间)下标从左到右0~N-1;下标从右到左-1~-N | lrange listkey 0 2 |
67 | | lindex {key} {index} | 获取列表指定索引下标的元素index=-1即为最后一个元素 | lindex listkey 1 |
68 | | llen {key} | 获取列表长度 | |
69 | | lpop {key} | 从左侧弹出元素 | |
70 | | lrem {key} {count} {value} | 从列表中找到等于value的元素并删除,根据count分为三种情况:count>0:从左到右删除最多count个元素;count<0:从右到左删除最多count绝对值个元素count=0:删除所有 | lrem listkey 4 a |
71 | | ltrim {key} {start} {end} | 按照索引范围修剪列表(只保留start~end) | ltrim listkey 1 3只保留第2个到第4个元素 |
72 | | lset {key} {index} {newValue} | 将列表中下标为index的元素设置为newValue | lset listkey 2 python |
73 | | blpop {key} {key}……{timeout} | 如果列表为空,客户端等到timeout秒后返回;如果列表不为空,客户端立即返回; | blpop list:test 3lpush+brpop可以实现阻塞队列 |
74 |
75 |
76 |
77 |
78 |
79 | ## 集合Set
80 |
81 | | **命令** | **含义** | **备注** |
82 | | ------------------------------------- | ------------------------------ | ------------------------------------------------------------ |
83 | | sadd {key} {element} {element}…… | 添加元素到集合key中 | sadd myset a b c |
84 | | srem {key} {element} {element}…… | 删除元素 | |
85 | | scard {key} | 计算元素个数 | |
86 | | sismember {key} {element} | 判断元素element是否在集合key中 | 在就返回1,不在返回0 |
87 | | srandmember {key} [count] | 随机从集合返回指定个数元素 | count是可选参数,不写默认1 |
88 | | spop {key} | 从集合随机弹出元素 | |
89 | | smembers {key} | 获取全部元素 | 返回结果是无序的可以考虑用sscan来代替 |
90 | | sinter {key} {key}…… | 求多个集合的交集 | |
91 | | sunion {key} {key}…… | 求多个集合的并集 | |
92 | | sdiff {key} {key}…… | 求多个集合的差集 | |
93 | | sinterstore {destination key} [key……] | 将交集、并集、差集的结果保存 | 存在destination key中sinterstore user:1_2:inter user:1:follow user:2:follow |
94 |
95 |
96 |
97 |
98 |
99 | ## 有序集合Zset
100 |
101 | | **命令** | **含义** | **备注** |
102 | | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ |
103 | | zadd {key} {score} {member} {score} {member}…… | 添加元素到集合key中 | sadd user:ranking 251 tom |
104 | | zrem {key} {member} {member}…… | 删除元素 | |
105 | | zcard {key} | 计算元素个数 | |
106 | | zscore {key} {member} | 计算某个成员的分数 | |
107 | | zrank {key} {member} | 按分数从低到高返回member在key中的排名(从0开始计算) | zrank替换为zrevrank表示从高到低 |
108 | | zincrby {key} {increment} {member} | 给成员member增加increment分数 | zincrby user:ranking 9 tom |
109 | | zrange {key} {start} {end} [withscores] | 返回指定排名范围的成员,从低到高返回。withscores可选参数,加上就会同时返回成员分数 | zrange替换为zrevrange表示从高到低返回 |
110 | | zcount {key} {min} {max} | 返回指定分数范围的成员个数 | zcount user:ranking 221 200 |
111 | | zremrangebyrank {key} {start} {end} | 删除指定排名内的升序元素 | zremrangebyrank user:ranking 0 2 |
112 | | zremrangebyscore {key} {min} {max} | 删除指定分数范围的成员 | zremrangebyscore user:ranking 250 +inf删除250分以上的成员返回结果为成功删除的个数 |
113 | | zinterstore {destination key} {numkeys} {key……} [weight……] [aggregate sum\|min\|max] | 做交集。(并集类似) | {key……}:需要做交集计算的key[weight……]:每个key的权重,默认1[aggregate sum\|min\|max]:计算成员交集后,分值score可以按照求和、最小值、最大值进行汇总,默认sum |
114 |
115 |
116 |
117 |
118 |
119 | ## 键管理
120 |
121 | #### 单个键管理
122 |
123 | | **命令** | **含义** | **备注** |
124 | | -------------------------- | ----------------------------- | ------------------------------------------------------------ |
125 | | rename {key} {newkey} | 重命名键 | 如果在rename之前,newkey已经存在,那么它的值会被覆盖。为了防止强行rename,可以使用renamenx。重命名时会del旧键,小心阻塞(旧值太大) |
126 | | randomkey | 随机返回一个键 | |
127 | | expire {key} {seconds} | 键在seconds秒后过期 | expire改pexpire可以把秒级改为毫秒级。如果key不存在,返回0;如果过期时间为负,键立刻删除。 |
128 | | expireat {key} {timestamp} | 键在秒级时间戳timestamp后过期 | expireat改pexpireat可以把秒级改为毫秒级 |
129 | | persist {key} | 删除过期时间(设为永久) | |
130 | | ttl {key} | 查看key的过期时间 | 返回-1:没有设置过期时间;返回-2:键不存在。ttl改pttl可以把秒级改为毫秒级。 |
131 |
132 | redis不支持二级数据结构(例如hash,list)内部元素的过期功能
133 |
134 |
135 |
136 | #### 键迁移
137 |
138 | **move**
139 |
140 | `move key db`
141 |
142 | move用于在Redis内部多个数据库之间进行数据迁移,一般用不到。
143 |
144 | **dump + restore**
145 |
146 | `dump key`
147 |
148 | `restore key ttl value`(ttl代表过期时间)
149 |
150 | dump + restore可以实现在不同Redis实例之间进行数据迁移的功能,迁移过程分为两步:
151 |
152 | 1. 在源Redis,dump命令将键值序列化,格式采用RDB;
153 | 2. 在目标Redis,restore命令将上面序列化的键值复原,其中ttl参数代表过期时间,ttl=0代表不过期。
154 |
155 | 注意:
156 |
157 | - 迁移过程并非原子性的,而是通过客户端分布完成。
158 | - 迁移过程是开启了两个客户端连接,所以dump结果不是在源Redis和目标Redis之间传输。(通过应用程序等)
159 |
160 | 伪代码示例:
161 |
162 | ```java
163 | Redis sourceRedis = new Redis("sourceMachine",6379);
164 | Redis targetRedis = new Redis("targetMachine",6379);
165 | targetRedis.restore("keyName",0,sourceRedis.dump(key));
166 | ```
167 |
168 | **migrate**
169 |
170 | - migrate是将dump、restore、del三个命令进行组合,简化操作流程。
171 | - migrate具有原子性。
172 | - migrate对于Redis Cluster水平扩容起到重要作用。
173 |
174 | **三种迁移命令的比较**
175 |
176 | | **命令** | **作用域** | **原子性** | **支持多个键** |
177 | | -------------- | --------------------------- | ---------- | -------------- |
178 | | move | Redis实例内部(不同库之间) | ✔ | ✘ |
179 | | dump + restore | Redis实例之间 | ✘ | ✘ |
180 | | migrate | Redis实例之间 | ✔ | ✔ |
181 |
182 |
183 |
184 | #### 渐进式键遍历
185 |
186 | 使用**scan**命令:
187 |
188 | **scan cursor [match pattern] [count number]**
189 |
190 | - cursor
191 |
192 | - - 游标。
193 | - 第一次遍历从0开始,每次scan返回当前游标值
194 | - 直到返回的游标值为0,表示遍历结束
195 |
196 | - match pattern
197 |
198 | - - 做模式的匹配
199 |
200 | - count number
201 |
202 | - - 表明每次要遍历的键个数
203 | - 默认为10
204 |
205 | **scan可以解决keys命令可能产生的阻塞问题**
206 |
207 | 如果在scan过程中有键的变化(增删改)可能导致遍历出现问题(新键没遍历到,重复遍历等)
--------------------------------------------------------------------------------
/docs/tech-life/今天我收到了蚂蚁金服实习offer.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | 在今天,我收获了蚂蚁金服A评级的实习录用offer。从开始面试到拿到口头offer(4x技术面+1xHR面)战线大约有半个月,从拿到口头offer到收到正式录用邮件大概又是半个月。
4 |
5 | 思前想后,决定还是为自己做一个整理与总结。一方面是回顾并记录自己的努力过程,一方面也是希望对后来者起到一些帮助。
6 |
7 | 前方高能预警,本篇文章要通读可能要很久。
8 |
9 | 以下内容普适于**技术岗**,非技术岗请部分参考。
10 |
11 |
12 |
13 | 目录
14 |
15 | - [如何进行知识积累](#如何进行知识积累)
16 | - [如何把握实践与理论的天平](#如何把握实践与理论的天平)
17 | - [我应该如何整理笔记](#我应该如何整理笔记)
18 | - [怎么复习才不会忘](#怎么复习才不会忘)
19 | - [面试前](#面试前)
20 | - [怎么写出让人眼前一亮的简历](#怎么写出让人眼前一亮的简历)
21 | - [如何突击面试](#如何突击面试)
22 | - [面试前焦虑该怎么办](#面试前焦虑该怎么办)
23 | - [面试开始](#面试开始)
24 | - [自我介绍到底要怎么说](#自我介绍到底要怎么说)
25 | - [面试中](#面试中)
26 | - [技术面试的时候应该注意些什么](#技术面试的时候应该注意些什么)
27 | - [面试尬场怎么办](#面试尬场怎么办)
28 | - [如何学会埋坑](#如何学会埋坑)
29 | - [面试结束](#面试结束)
30 | - [面试官:“你有什么想问我的吗”,该说什么?](#面试官:"你有什么想问我的吗",该说什么?)
31 | - [我怎样才能知道我是否通过了](#我怎样才能知道我是否通过了)
32 | - [我两年半的技术人生](#我两年半的技术人生)
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | ## 如何进行知识积累
41 |
42 | 在讲面试技巧之前,显然知识积累是不可或缺的。不然即使面试侥幸通过,在之后的工作中也会体验极差。
43 |
44 | ### 如何把握实践与理论的天平
45 |
46 | 实践是什么?
47 |
48 | - 比如你`new Object()`初始化了一个对象来使用;
49 |
50 | - 比如你用HashMap结构作为容器存储了一些数据;
51 |
52 | - 比如你拿SpringBoot搭建了一个web网站后台;
53 |
54 | - 比如……
55 |
56 |
57 |
58 | 理论是什么?
59 |
60 | - 初始化对象有什么代价?背后的底层实现逻辑是怎么样的?初始化太多对象可能导致什么问题?为什么我经常初始化对象,明明是空间消耗大,反而导致程序在运行时间上变得缓慢?
61 | - HashMap适合什么场景?我现在这个场景真的是用HashMap最合适吗?运用的时候有没有什么需要注意的?有时候遇到一些特殊需求,在HashMap基础上可以再优化性能吗?
62 | - 天天用SpringBoot,IOC、AOP到底是什么概念,背后怎么实现的?它们适用场景如何,我的用法是最佳实践吗?会不会有什么弊端,导致在生产环境出现问题?
63 | - ……
64 |
65 |
66 |
67 | 实践与理论的学习,到底应该侧重实践一些呢,还是侧重理论一些呢?
68 |
69 | 都说**实践与理论要两相结合,缺一不可**,在我看来这是一句废话。重点是如何权衡两者,并且在不同的发展阶段,两者的**侧重比例是否又要发生变化**呢?
70 |
71 | 在我看来,实践**决定方向**,理论**填补细节**。
72 |
73 |
74 |
75 | #### 1、首先要实践,然后填补理论
76 |
77 | 程序员**首先要实践**,才能够收获基本的技术视野和处理问题的能力,这两者都是不可或缺的。
78 |
79 | 有了基本的视野之后,就可以根据自己学习的方向,去**填补你的细节**,例如:
80 |
81 | 1. 我学习了Java,会写一些应用程序了,也知道如何利用应用服务层的Java来对数据库层的数据做一些处理,那么接下来:
82 | 1. 对数据怎么处理会更快(这里可能就会引出多线程,然后就可能引出线程池,又引出JDK提供的线程池有什么并发问题,怎么解决,然后可能又引入并发包,一下子串出好多)
83 | 2. 各种优化(比如初始化太多对象导致频繁GC,全局变量太多导致占存一直很高blabla)
84 | 2. 我学习了MySQL,会用SQL语句操作数据了,知道建索引可以加快访问速度了,那么接下来:
85 | 1. 在SQL语句上是不是也可以做一些优化来提升性能(比如很有名的**延迟关联**)
86 | 2. 索引到底应该怎么建才好(这就涉及到索引的很多知识,比如B+树,比如一些匹配原则例如**最左适配原则**等等)
87 |
88 |
89 |
90 | 这种细节的填补是有**逻辑性**的,从我上面举的两个例子就可以看得出来。这种**逻辑性**的存在会使你在学习的过程中不会感到虚浮,会发现这些理论都是切实有用且有价值的。
91 |
92 | 但这样的理论学习还不够,因为它**不系统**。充分的发散思维使你能够在实践之外追求到很多理论知识,但那些你发散不到的部分就接触不到了。这个时候你就需要系统性的学习,例如**读书**。
93 |
94 | 当你第一次学会实践,就好像在自己的脑海中制作了一个**知识星球**。它里面几乎是空白的,但是球体本身存在,帮助你去界定、区分知识——借助这个球体的框架,当你在遇到一个新知识的时候,你会它有一个模糊的概念:这个知识到底是有用知识还是无用知识,它又在哪个范畴或层面里?
95 |
96 | 所以借助了一开始的实践,然后你再去逻辑地发散、或者系统地读书的时候, 就不会让新知识成为无根之萍,而是切实地进入你的知识星球中,成为你**知识架构**的一部分。
97 |
98 |
99 |
100 | #### 2、理论再返回到实践中
101 |
102 | 知识架构建立起来了,但它没有经历实践的检验,就依然是**不可用**的。
103 |
104 | 一方面在于我们学习的理论知识可能是**过时**的,甚至是**谬误**的;另一方面我们可能以为自己懂了但其实没懂,这一点相信大家都能理解。
105 |
106 | 那么如何实践呢?有些理论可能是很难实践出来的,但是至少我们要对能实践的一部分去做一下尝试。就比如简单的JVM排查和调优,通过MySQL EXPLAIN去检查执行计划并实践优化等等。
107 |
108 |
109 |
110 |
111 |
112 | ### 我应该如何整理笔记
113 |
114 | 首先要强调**笔记**是很重要的。至少我认识的那些能够面试进大厂的同学,每个人都会为自己做笔记。
115 |
116 | 而至于到底应该如何**整理**笔记,我认为最重要的有两点:一是**分门别类**,二是**控制粒度**。
117 |
118 |
119 |
120 | 首先是**分门别类**:
121 |
122 | 我们一定要能够清晰地把我们要整理的知识模块化,比如说JVM基础,我们可以这样分模块:
123 |
124 | - 运行时数据区
125 | - 垃圾回收
126 | - 类加载
127 | - JMM
128 | - JVM调优
129 |
130 | 然后分别根据每一个小模块,单独整理一篇笔记。
131 |
132 | 并且在篇末,或者另开一篇笔记,专门记录针对这个模块的比较**大而广的问题**。
133 |
134 | 例如运行时数据区,我们就可以记录这样一些问题:
135 |
136 | - 讲讲JVM运行时数据区的各个组成,是什么,有什么用
137 | - 讲讲对象。它是如何创建的,里面包含一些什么信息,如何定位到对象
138 |
139 | 记录这种大的问题,有助于我们进行**自测**。不要问自己太多细小的问题,除非你切实觉得它很重要。什么叫细小的问题呢?比如:**类里静态的基本数据类型存在方法区还是堆里?**这种问题枚举你是枚举不完的,它其实已经包含在了”**讲讲JVM运行时数据区的各个组成,是什么,有什么用**“这个问题里。
140 |
141 |
142 |
143 | 其次是**控制粒度**:
144 |
145 | 什么叫控制粒度?其实就是控制你笔记记录的详细程度。
146 |
147 | 如果你笔记中对于知识点的描述非常简洁,带来的好处是阅读起来就会很快,坏处是可能在阅读时导致你忽略掉一些本不该忽略的细节,或者甚至是:”咦,我这记的是啥,我怎么看不懂了。“
148 |
149 | 那如果记得太详细呢?很显然,就会导致阅读起来非常繁琐,可能达不到一个理想的**迅速复习**的效果。
150 |
151 | 那到底应该控制在一个什么程度上呢?
152 |
153 | 我认为一篇良好的笔记应该满足以下两个条件:
154 |
155 | 1. 这段笔记切实提到了所有应该提到的知识点,不需要我去联想;
156 |
157 | 2. 这段笔记对于这些知识点都有简单的描述性文字,并且能够言简意赅,尽可能以列表列举的形式,不要有叙述性的内容。
158 |
159 | tips:什么叫叙述性内容?
160 |
161 | 举个例子,比如下面这是一段对于垃圾回收算法里的标记-清除算法的笔记:
162 |
163 | > 标记-清除算法:
164 | >
165 | > 标记出所有需要回收的对象,根据标记统一回收。
166 | >
167 | > 问题:1. 低效率
168 | >
169 | > 2. 产生大量不连续内存碎片
170 |
171 | 带有叙述性内容的写法是怎么样的呢:
172 |
173 | > 标记-清除算法:
174 | >
175 | > 它的基本原理是,标记出所有需要回收的对象,然后根据标记统一回收。
176 | >
177 | > 它有两个问题,一是在于效率比较低,二是在于可能产生大量不连续的内存碎片。
178 |
179 | 非常显然,阅读前者的效率远高于后者。
180 |
181 | 整理笔记时,我们尽可能省去不必要的铺垫,例如”它的基本原理是“这句话就是完全不必要的。我们也可以尽可能省去一些联结词,例如”首先“、”然后“、”最后“这种。以及对于一些枚举性内容,我们尽可能采取列表而非文字形式表达,会更加直观易懂易记。
182 |
183 |
184 |
185 |
186 |
187 | ### 怎么复习才不会忘
188 |
189 | 每天都反复复习当然可以保证你不会忘,但会非常疲惫而且费时。
190 |
191 | 严格遵守网上的某些号称贴合人类记忆曲线的复习方式,坚持起来压力很大,最终往往也很难达到自己理想的效果。
192 |
193 | 实际上这种所谓”曲线“是有一定道理的,但是也要结合我们每个人自身的特殊情况,去找到最适合自己的一种记忆方式。
194 |
195 | 我认为这样一种记忆法是比较有效的:
196 |
197 | 从第一次复习并记忆的时候开始算,隔比较短的一段时间再复习并记忆一次,隔不太短的一段时间再复习一次,然后坚持每隔较长的一段时间就复习一次。
198 |
199 | 那么这个”**比较短的一段时间**“、”**不太短的一段时间**“、“**较长的一段时间**”又分别是多久呢?我们可以自己来决定。但需要知道的是,我们的复习间隔时间应该是不断变长的,直到一个比较稳定的值。
200 |
201 | 比如我个人的话,就喜欢第一次记忆完后,两小时再复习一次,隔一天再复习一次,然后隔四五天复习一次,之后可能就一直是7-10天会复习一次。
202 |
203 | 无需刻意,比如我本来打算三天后复习一次,但是三天后突然有什么事耽搁了。所以我提前到前一天,或者滞后到后一天,又能怎么样呢?记忆是量变的过程,一天两天的偏移根本不会有什么质变可言。
204 |
205 | 但让记忆周期大抵符合一个慢慢变长的规律,我想是有必要的。而如果你觉得最后每隔7-10天复习一次都会压力很大,那你也可以半个月甚至二十天才看一次。不过这样的话,等面试要来临的时候,你可能就难免需要简单突击一下了。
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 | ## 面试前
214 |
215 | ### 怎么写出让人眼前一亮的简历
216 |
217 | 按照一般流程来说,你的简历会先给懂一点技术/不懂技术的HR初审,然后再交给研发的同事过审,经过这两层都ok了,才会给你进面试的流程。
218 |
219 | 也就是说,你的简历不仅要让专业人士看着厉害,还要让非专业人士看着也厉害。
220 |
221 | 那么作为技术人员,简历到底应该怎么写呢?
222 |
223 |
224 |
225 | #### 1、综述
226 |
227 | 我们不妨**先概括后具体**,先总体看一下简历应该写一些什么东西,写多长篇幅,排版成什么样子,然后再分析每个模块应该如何去写。
228 |
229 | ##### 写什么东西
230 |
231 | 一个正常的简历应该有如下内容:
232 |
233 | - 基本信息及联系方式
234 | - 学习经历
235 | - 工作经历(或实习经历)
236 | - 项目经历
237 | - 专业技能
238 | - 自我评价(这个不一定要有,看前面篇幅)
239 |
240 | ##### 写多长篇幅
241 |
242 | 作为技术人员,我觉得简历1-2页就好,3页未免太过冗长。
243 |
244 | ##### 排版成什么样子
245 |
246 | 首先格式上来说,现在一般都是网申,也就是提交电子材料,所以HTML或者PDF格式为佳。
247 |
248 | 具体排版的话,尽可能简洁明了,不要花里胡哨。技术人,又不是去做UI设计,搞那么好看,人家也不会欣赏,反而觉得你不务正业。我个人建议简历的好看程度,达到markdown能支持的极限就可以了,不要超过markdown的能力。
249 |
250 |
251 |
252 | #### 2、基本信息及联系方式
253 |
254 | 可能会包括以下一些信息:
255 |
256 | - 姓名,电话,邮箱:这几个是必须的,不多说了。
257 | - 求职意向:单独列这条出来,我建议要写。不然你本来做后端的,人家给你安排到测试开发,你哭都来不及。
258 | - 个人网站/博客/GitHub:如果有厉害的一定要记得写,如果很水的话建议别写了。
259 |
260 |
261 |
262 | #### 3、学习经历
263 |
264 | 可能会包括以下一些信息:
265 |
266 | - 学历(或者毕业年份)
267 | - 绩点:如果不好看,请不要写,不要写,不要写
268 | - 专业:如果不是计算机相关专业,请不要写,不要写,不要写
269 | - 奖项:尽可能多写,从高到低排。先写厉害的,最后写不厉害的。如果没什么拿的出手的奖项,建议干脆什么也不要写。不要孤零零写个什么XX大学优秀团员上去,还不如不写。
270 |
271 |
272 |
273 | #### 4、工作经历(或实习经历)
274 |
275 | 这里就如实说就好,切记一定要准确,包括在职时间也写明白,一两个月的偏差也是在给自己埋炸弹。
276 |
277 | 应该包括以下信息:
278 |
279 | - 在职时间:例如2018.2~2019.3
280 | - 工作单位:如果不是大家熟知的大厂,最好附上行业
281 | - 部门:这个是可选项,不一定要写。如果是厉害的部门请记得写,比如腾讯微信
282 | - 岗位:建议具体。如果公司里岗位就叫“研发工程师”,你可以写成“JAVA研发工程师”或者“GO研发工程师”之类的。
283 |
284 |
285 |
286 | #### 5、项目经历
287 |
288 | 这是第二重要的部分!!第一重要的部分是下面的专业能力。但这也是决定你是否能简历过审的很重要的一部分,并且是可能导致面试官疯狂追问你的来源。
289 |
290 | 一般写2-3个项目经历比较好,相对来说,要挑最厉害的、最对口的、最近的。
291 |
292 | 那么每个项目经历中,应该写一些什么呢?
293 |
294 | - 项目名称
295 |
296 | - 项目简介:1-2句话就好,让别人知道你这是个什么项目。比如是个后台管理系统?比如是个电商平台?之类的
297 |
298 | - 个人技术工作:请以列表形式列举自己的核心工作,不要大段叙述性文字!!
299 |
300 |
301 |
302 | 后面是一些可选项,不一定要写:
303 |
304 | - 技术关键词:项目中涉及到的技术关键词
305 |
306 | - 项目中的收获
307 |
308 | - 项目成果:项目上线后有什么成果。比如抗住了多大的QPS,比如做到了多大的规模等等。请注意这两者是有区别的,前者可能更强调峰值,后者是长期的稳定性。当然还可以有其他的,但最好要有技术视角。
309 |
310 |
311 |
312 | 给一个优秀的范例:
313 |
314 | 
315 |
316 |
317 |
318 | 如果觉得这个有点太高大上了,再看一个普通一点的:
319 |
320 | 
321 |
322 |
323 |
324 | #### 6、专业技能
325 |
326 | 专业技能方面应该尽可能讲得全面,把自己确定会的一个都不要漏掉。
327 |
328 | 但是一定要和岗位对口。比如你投个Java后台,你非得写个摄影技能上去,反正我个人感觉不是很好。
329 |
330 | 如果你不知道怎么写,可以看一下岗位描述。一般投递简历前你看到的岗位描述上都会有对于技术要求的说明,比如:
331 |
332 | > =======我们的要求=======
333 | >
334 | > 1. 全日制211本科以上学历,计算机相关专业,毕业时间2020.11-2021.10
335 | > 2. JAVA基础扎实,理解io、多线程、集合等基础框架,对JVM原理有一定的理解
336 | > 3. 熟悉分布式、缓存、消息、搜索、推荐等技术,并能合理应用,解决实际问题
337 | > 4. 学习能力强,对代码质量及系统性能具备精益求精的精神
338 | > 5. 良好的团队沟通协同能力,抗压能力,勇于接受挑战
339 | > 6. 有作品或者实习经验优先
340 |
341 | 我们可以根据岗位的要求,结合我们自身的优势,来写我们的专业技能。像这样:
342 |
343 | 
344 |
345 |
346 |
347 |
348 |
349 | ### 如何突击面试
350 |
351 | 我个人觉得,突击面试是在面试前1-10天的这个阶段。
352 |
353 | #### 1、笔记突击复习
354 |
355 | 这个是毋庸置疑的。之前在[怎么复习才不会忘](#怎么复习才不会忘)里说,最后坚持每7-10天复习一次。如果你坚持了这种频率,我觉得笔记突击复习对你来说几乎是不需要的。不过如果你觉得心有不安,可以在突击阶段每天复习一次。
356 |
357 | 切记一点,笔记突击复习时,请记得自己问自己问题,然后模拟一下在面试官面前你会怎么回答。别只是记忆,你还要学会如何表达。
358 |
359 |
360 |
361 | #### 2、针对性复习
362 |
363 | 针对性复习包含两方面:**把握公司面试特点**+**刷特定面经**。
364 |
365 | 首先是把握公司特点。打个比方,如果你去面试字节跳动,可能就需要多准备一点算法、网络,他们很喜欢问这些。如果你去面试美团,你可能就更需要准备一些JUC并发包、框架。
366 |
367 | 把握公司特点很重要,你会发现即使同样是面试JAVA岗位,不同公司的面试内容差别也会很大。
368 |
369 | 甚至不只是公司。到部门层面,差别也会很大。某公司A部门的JAVA岗,和该公司B部门的JAVA岗,就是要求不一样的。也许一个更注重并发能力,一个更注重排查调优能力等等。这一点你可以从岗位要求里去了解,或者问部门里相关的人员。
370 |
371 |
372 |
373 | 其次是刷特定的面经。现在网上有很多人分享面经。可能不一定能直接找到你现在面试的部门的面经,但你至少可以找到同一家公司下、业务类型相近的面经。把他们题目拿过来,给自己模拟面试一遍。和前面说笔记突击复习一样,模拟你的表达,别只是心里想一想答案就过去了。
374 |
375 |
376 |
377 | #### 3、每日一道算法
378 |
379 | 如果你面试中可能会出现算法题,建议你在突击阶段每天一道LeetCode以保持手感。不过这因人而异,有些人即使很久不做算法,临阵磨枪也不虚。
380 |
381 |
382 |
383 |
384 |
385 | ### 面试前焦虑该怎么办
386 |
387 | 什么深吸一口气?站在阳台看看窗外?我觉得效果不是很大。
388 |
389 | 深呼吸在面试前半小时是有效的,它可以缓解你面试前过分紧张的情绪。但它是缓解紧张的,无法缓解焦虑。
390 |
391 | 焦虑的源头在于:**害怕意外的发生**。我觉得缓解焦虑最有效的方式就是,为自己**规划好一切**。
392 |
393 | 在突击面试之前,时间尚早,相信你也不会有太多焦虑。当突击面试阶段开始,你就要开始学会为自己规划。让一切有条不紊地进行,你会发现你不再会为面试和其结果感到焦虑,因为你知道你自己做到了最好。
394 |
395 | 那如何规划呢?
396 |
397 | 不妨给个例子:
398 |
399 | 比如我在下周六(2020-04/18)是阿里的一面。而今天是周日(2020-04-12),接下来我怎么安排呢?
400 |
401 | > 2020-04-12 周日
402 | >
403 | > 复习单线程容器、JUC包,整理出相关的大而广的面试问题,进行自测。
404 | >
405 | > 2020-04-13 周一
406 | >
407 | > 复习网络、操作系统
408 | >
409 | > 2020-04-14 周二
410 | >
411 | > 复习JAVA常见面试题、JVM
412 | >
413 | > 2020-04-15 周三
414 | >
415 | > 复习MySQL和Redis
416 | >
417 | > 2020-04-16 周四
418 | >
419 | > 复习Spring、IO、设计模式
420 | >
421 | > 2020-04-17 周五
422 | >
423 | > 网上查阅面经自测,做两道算法恢复一下手感
424 | >
425 | > 2020-04-18 周六
426 | >
427 | > 盯着自己准备好的各种大而广的问题反复车轮式复习,直到面试前半小时。
428 | >
429 | > 半小时里再背一遍自我介绍,复习一下简历里的项目。
430 | >
431 | > 迎接面试。
432 |
433 |
434 |
435 | 如果这样规划以后,依然让你感到十分焦虑,很简单:把规划**再细化**。
436 |
437 | 你会发现,当规划的细致程度达到某种阶段以后,你的焦虑会转化成压力和动力,不会再让你手足无措。
438 |
439 |
440 |
441 | 最后说一个小tip。其实让你面试前不焦虑还有一个非常简单的办法,就是多投递简历,多面试,面多了你就~~会感到麻木,~~不会焦虑了。
442 |
443 |
444 |
445 |
446 |
447 |
448 |
449 | ## 面试开始
450 |
451 | ### 自我介绍到底要怎么说
452 |
453 | 如果是一面/二面,一般是纯技术面,这个时候我建议可以遵照这样一种自我介绍模板:
454 |
455 | - 自报家门
456 | - 介绍自己的技术方向和技术能力
457 | - 我主攻的方向是XX,从技术上来说,我对于XX、XX(枚举一些技术点)比较熟悉(以引导面试官问你熟悉的内容)
458 | - 介绍项目经历(什么项目,在里面做什么事情,有什么难点,怎么克服的,有什么收获)
459 | - 我在之前XX(读大学?在XX工作?之类的)的时候,很喜欢并善于研究 / 做过一些有价值的项目,比如blabla。期间遇到过一个问题至今印象深刻,blabla。经过这个项目,我吸收了一些blabla的知识。
460 | - tips:在**介绍项目经历**的阶段中,“**在里面做什么事情**”是最重要的模块,一定要讲清楚,做什么事情决定你在面试官第一印象中的高度。而“**有什么难点,怎么克服的**”是给你一个埋坑的机会,让面试官更有可能在这个点上去追问你。
461 | - 收尾
462 | - 希望在未来,可以把这些知识(之前项目中提到的知识)运用在工作中。
463 | - 也希望在本次面试中,可以得到面试官的一些建议和指导。
464 |
465 |
466 |
467 | 如果是三面/四面/HR面,并且你切实觉得可能不再那么纯技术了,你的自我介绍可以向软实力角度偏移,这里拿我的自我介绍给个例子:
468 |
469 | tips:整理你自己的自我介绍的时候,请切记**不要太书面语**,背起来不舒服不说,真正到自我介绍的时候也会显得生硬。
470 |
471 | > 您好,我是来自XX大学XX专业的XX。
472 | >
473 | > 我想从技术理论、项目实践、工作能力上来简单介绍一下自己。
474 | >
475 | > 首先是技术理论。我主攻的方向是Java后台。技术上来说,我对于Java基础、JVM、并发容器、线程池等等还是比较熟悉的,另外在框架方面用SpringBoot比较多,读过Spring的源码。而数据库层面的话,磁盘数据库中我对于MySQL尤其是InnoDB引擎比较熟悉,内存数据库中对于Redis比较熟悉。
476 | >
477 | > 而在项目实践方面,我对于理论和实践的结合一直是比较看重的。从大一入学两个月我就有带领大一的学生队伍参加院里的项目比赛,之后陆陆续续到今天累计也做了不下十几个项目了。而在这些项目中,在综合方面我主要担任的也都是一个负责人的角色,而在技术方面我主要做的则都是Java后台的部分。我开过会议,出过文档,写过代码,也参加过有好几百人作为听众的大型答辩,应该说不管在硬实力还是软实力上,都得到了很不错的锻炼。
478 | >
479 | > 最后是我的工作能力。我现今任我们专业XX班的班长,已经历时一年半,对于组织工作应该说是非常熟悉了。另外,我也有参与学校里一个较大型的信息技术工作室,出任其中的技术部负责人,偶尔会带领学弟学妹们进行一些项目实践,以及参与引导部门及工作室的规划安排。事务很多很杂,也培养了我强大的规划力和执行力,相信这也能够使我在未来的工作中得心应手。
480 |
481 |
482 |
483 |
484 |
485 |
486 |
487 | ## 面试中
488 |
489 | ### 技术面试的时候应该注意些什么
490 |
491 | 要从**表达**和**内容**两方面来讲。
492 |
493 | #### 表达
494 |
495 | 表达切记一定要**口齿清楚**,**逻辑清晰**。
496 |
497 | **口齿清楚**
498 | 口齿清楚这一点相信大家都能领会。但是也不排除有些小朋友一紧张口齿就不利索,这个一定要注意。
499 |
500 | 更有甚者有时候遇到不太确定的问题,想要以口齿不清来蒙混过关,这种更不可取,基本是搬石砸脚。
501 |
502 | **逻辑清晰**
503 |
504 | 如何保证这一点呢?有时候当遇到一个措手不及的问题,很难保证我们的逻辑又快又清晰。所以最简单的方式,就是用**速度的牺牲**来换取逻辑的明了。甚至有时候你可以短暂地沉默一会儿,好好整理一下思路,千万别张口就来,导致说得乱七八糟不成体系。
505 |
506 |
507 |
508 | #### 内容
509 |
510 | 对于一些切入点非常小的问题,直接回答就可以。比如说:
511 |
512 | > Q:对象的锁信息是存在哪里的?
513 | >
514 | > A:在对象头MarkWord里
515 |
516 |
517 |
518 | 但是对于一些切入点比较大的问题。例如:
519 |
520 | > Q:讲讲垃圾回收?
521 | >
522 | > Q:集合里ArrayList和LinkedList有什么区别?
523 |
524 | 回答的内容切记不可过于简短,但也不能胡乱堆砌。最好的方式是**水平扩展**或者**垂直扩展**。
525 |
526 | 不妨就拿上面的一个问题来举个例子。比如说讲讲垃圾回收。
527 |
528 | **水平扩展**的话,应该怎么讲呢?
529 |
530 | > A:
531 | >
532 | > 有关垃圾回收,我们首先要考虑两个问题。一是如何判断对象可回收,二是用什么样的方式来回收。
533 | >
534 | > 首先对于前者,有引用计数法和可达性分析两种方法,它们分别……(讲讲它们的含义,优缺点)
535 | >
536 | > 而对于后者,市面上主要有标记-清除,标记-整理,复制三种回收算法,它们分别……(讲讲含义,优缺点)
537 | >
538 | > 结合这些算法,市面上就出现了很多垃圾收集器,例如Serial,ParNew,CMS,G1……(顺便讲讲它们的回收逻辑,优缺点)
539 | >
540 | > 面试官:(暗自点头)
541 |
542 | 水平扩展也就是从一个问题出发,把与它相关的整棵知识树或者整颗知识树的一部分(如果树太大的话)讲出来,体现你的知识是有架构有体系的。
543 |
544 |
545 |
546 | **垂直扩展**的话,又应该怎么讲呢?
547 |
548 | > A:
549 | >
550 | > 垃圾回收的话,自JDK1.8后,市面上就非常流行G1垃圾回收器了。它是不分新生代和老年代的,基本原理是……(讲讲含义,优点)
551 | >
552 | > 但是垃圾的频繁回收势必会导致用户体验的下降,虽然G1已经很优秀了,作为开发者我们还是需要关注JVM的优化(话锋一转,开始走向深度)
553 | >
554 | > (讲一些具体的优化策略)
555 | >
556 | > 面试官:(暗自点头)
557 |
558 | 垂直扩展主要是需要自行找到一个切入点。或者面试官已经把切入点给到你了,那么接下来你要引入自己的思考,按照一个没有漏洞的逻辑走向深度,体现你是勤于思考并且善于发现并解决问题的人。
559 |
560 |
561 |
562 |
563 |
564 | ### 面试尬场怎么办
565 |
566 | 首先如果是因为你答不上来而尬场,你就乖乖说你不会,千万别强撑。
567 |
568 | 如果不是这个情况,是面试官一时沉默的话:
569 |
570 | 有时候面试官可能很忙,他可能都没来得及看完你的简历就来给你面试。换言之,他并没有做太多的准备。所以临时尬场也是非常正常的。
571 |
572 | 所以现在你要做的就是:让面试官不要尬场。
573 |
574 | 打个比方:
575 |
576 | > Q:你说你熟悉线程池,那你说说看线程池都有些什么重要的参数?
577 | >
578 | > A:比如有核心线程数、最大线程数、允许存活时间、阻塞队列等等(顺便简单说一下这几个参数都是什么含义)
579 | >
580 | > Q:嗯不错……(开始沉默)
581 |
582 | 一般面试官的沉默可能是你回答完这一个问题之后,他一时间想不到问什么。这个时候你可以继续沿着这个问题扩展下去:
583 |
584 | > A:关于线程池还有一些需要注意的地方。比如当一个任务添加进来的时候……(讲一下任务添加到线程池的流程)
585 | >
586 | > A:考虑到这样一个流程,所以我们在控制最大线程数、阻塞队列的时候是需要注意的。如果最大线程数设置过大,或者阻塞队列设置上限过长,可能导致OOM(又联系回前面参数的问题)
587 |
588 | 是不是联系回去不要紧,但是只要你依然在讲这个问题相关的知识树里的东西,就可以有效地缓解尬场,还会让面试官觉得:
589 |
590 | > 面试官:(嗯这小伙基础还挺扎实)
591 |
592 |
593 |
594 |
595 |
596 | ### 如何学会埋坑
597 |
598 | 埋坑是非常重要的一个技巧。
599 |
600 | 什么叫埋坑?就是在回答前一个问题的时候,适当地对面试官进行引导,让他能够问你想让他的问题。
601 |
602 | 面试前是最容易埋坑的。一是简历里的项目经历,专业技能;二是自我介绍阶段,讲一些自己擅长的技术点等等,引导面试官去问你。
603 |
604 | 而在面试的过程中,应该如何埋坑呢?
605 |
606 | 打个简单的比方。
607 |
608 | 比如现在面试官问你:”ArrayList和LinkedList有什么区别?“
609 |
610 | 你先中规中矩地回答。回答完之后,我们可以根据我们的擅长点做一些不同的策略:
611 |
612 | 如果你比较擅长**并发容器**,你可以说:
613 |
614 | > ArrayList和LinkedList都属于单线程框架,是并发不安全的。如果要并发安全的话,需要使用CopyOnWriteArrayList或者ConcurrentLinkedQueue
615 |
616 |
617 |
618 | 如果你比较擅长**线程池**,你可以说:
619 |
620 | > ArrayList和LinkedList分别是对数组和链表的封装。JAVA中比较重要的对于数组、链表的封装集合类还有线程池中的阻塞队列,例如ArrayBlockingQeque和LinkedBlockingQueue
621 |
622 |
623 |
624 | 当然了,埋坑只能启到一定的导向作用,最终决定权还是在面试官手上。不要太刻意,不然会适得其反。
625 |
626 |
627 |
628 |
629 |
630 |
631 |
632 | ## 面试结束
633 |
634 | ### 面试官:”你有什么想问我的吗“该说什么?
635 |
636 | 这个问题可以给几个例子:
637 |
638 | 1. 您对我有什么建议吗?
639 | 2. 您平时工作中主要做些什么呢?
640 | 3. 如果我有幸能够被录取的话,主要会做一些什么样的工作呢?
641 |
642 |
643 |
644 |
645 |
646 | ### 我怎样才能知道我是否通过了
647 |
648 | 我个人不建议在面试完后直接问面试官:”我通过了吗?“
649 |
650 | 而且面试本来就是双向选择,你是在众多offer中挑一个最喜欢的,人家也是在众多面试者中挑最好的那一批。
651 |
652 | 所以如果你想要知道自己是否通过了,最好的方式是从面试官对你的态度中窥探。
653 |
654 | 比如你最后问:”您平时工作中主要都做些什么呢?“
655 |
656 | 他给你回答得非常详细,而且还会主动跟你聊一点别的,比如告诉你进来以后会做什么之类的,那说明他还是很认可你的。
657 |
658 | 如果他只是简单说一下,有点敷衍的样子,不排除只是他工作忙的可能,但你最好也可以考虑一下,找找其他出路了。
659 |
660 | 另外,等面试结束后,如果你有推荐你去面试的推荐人或者HR的联系方式,你也可以直接问他们面试的结果。
661 |
662 |
663 |
664 |
665 |
666 |
667 |
668 | ## 我两年半的技术人生
669 |
670 | 最后附上我两年半的技术生涯,中间不免有些弯路,但总体还是令我满意的。希望给有需要的人以参考。
671 |
672 | 我的时间线:
673 |
674 | - 2017(入学)
675 | - 7-8月:参加社会上的Java语言基础培训课程,学习Java,基本上到可以简单使用Swing的程度,对于反射泛型等中高级知识一窍不通;
676 | - 10月:拉了五个也是大一的小伙伴参加学院的创新创业比赛,基于Java Swing写了一个井字策略游戏,拿到学院的优胜奖;
677 | - 11月:机缘巧合认识了一位研二的研究生,经他推荐进入一位教授的实验室做商业网站;
678 | - 12月:在研究生的带领下认识SpringBoot和Mybatis,原理一窍不通,但是可以写一点CRUD;
679 | - 总结:
680 | - Java语言最基础内容的使用比较熟练了
681 | - 有了可怜到几乎可以忽略不计的web后台开发经验
682 | - 当时学院算法学习氛围比较好,到年末我大概相当于共计刷了一百道LeetCode中等题
683 | - 2018(大一-大二)
684 | - 1-3月寒假:进入培训机构学习web前端,了解了html-css-js,学会ajax;
685 | - 4-6月:划水度过,我不知道我在干嘛,就好好上课;
686 | - 7-8月暑假:做一些基于SpingBoot的简单实践,初步学习Unity游戏引擎;
687 | - 9-10月:划水;
688 | - 11-12月:学校各种创新比赛启动,我都参加了,最后同时启动了大约6-7个校园小项目;
689 | - 总结:
690 | - 这是我多角度尝试的一年(比如web前端、游戏开发),因为我还并没有决定以后从事什么方向。大约在暑假后(9月),我才觉得自己未来应该会做Java后台;
691 | - 在后台技术上提高不多,只是熟练了那么一丢丢;
692 | - 项目启动很多,那个时候觉得项目很重要很能锻炼人,后来觉得其实做那么多差不多级别的项目并不是很有意义。
693 | - 2019(大二-大三)
694 | - 1-3月寒假:我启动了太多项目,CRUD崽忙不过来了!!
695 | - 3-6月:各种项目比赛遍地开花的几个月
696 | - 3月:两个创新培育项目,一个国创一个市创评级
697 | - 4月:上海市计算机应用能力大赛二等奖
698 | - 6月:校大夏杯创业大赛铜奖
699 | - 但只有我自己心里清楚,奖是拿得很不错,但是技术上其实都是一样的东西,而且非常简单非常基础。全是传统的小型单体web项目,业务驱动。
700 | - 还是3-6月:比我大两级的拿到蚂蚁金服正式offer的学长来讲了Java面经,我终于意识到自己太菜了。但是跟着大三春招的洪流,我也没忍住投了几个简历试试:
701 | - 携程正式批次:一站式面试,我不知道一站式是什么意思,去面了一面自我感觉良好,就直接走了!走了!没在那等着继续面下去,就没了。
702 | - 字节跳动 · 北京:视频面试,在第二面挂了,评价是”相对大二学生来说比较优秀,但是暂时还没达到实习岗位的要求“
703 | - 7月:买了一堆蚂蚁学长推荐的书籍,例如《高性能MySQL》、《深入理解Java虚拟机》、《Java并发编程实践》、《图解HTTP》等,开始有计划地阅读起来;
704 | - 8月:去安徽会场参加全国大学生计算机设计大赛的答辩,荣获一等奖。还是那句话,真没啥技术含量。但是提升了我答辩的能力~~和吹嘘的水平~~;
705 | - 9-12月:继续读书。
706 | - 总结:
707 | - 这是我从实践逐步转战理论学习的一年。之前的我太过于知其然而不知其所以然了,写项目都是不考虑原理的。自蚂蚁学长分享了Java面经之后,为我指出了方向。
708 | - 这一年我仔细阅读了《高性能MySQL》前七章,《深入理解Java虚拟机》近整本,也买了《MySQL技术内幕 InnoDB存储引擎》看完了一整本,做了很多笔记。
709 | - 我还买了很多其他杂七杂八的书,例如《轻量级微服务架构》、《图解TCP/IP》、《大型网站架构》等等,有些是几乎读不懂,有些是没怎么读,大抵算是这一年走的弯路。
710 | - 2020(大三)
711 | - 1月:疯狂整理、复习,中旬参加了携程的日常实习面试,通过了,年前进入携程实习;
712 | - 2月:过年几天还带了两本书回老家,但是真的学不进去。回来后因为疫情远程办公,同时不断进行复习与整理,准备春招;
713 | - 3月:开始进入春招面试阶段。只投了字节和阿里,都通过了;
714 | - 4月:一直到今天4.11,一直是静待offer。没有什么事情,就不疾不徐地学一学SpringCloudAlibaba,平时就~~谈谈恋爱~~,打打游戏,看看电影。
715 |
716 |
717 |
718 | 我两年半的技术生涯,主要走的是 实践学习 → 理论学习 → 实践理论相结合 这样一条路线。
719 |
720 | 一开始的实践学习就像是培训班生活,只不过是自我培训;
721 |
722 | 然后到理论学习层面,有幸听到蚂蚁学长的面经讲座,加上有之前的实践基础,让我的理论学习有方向感且不会太虚浮;
723 |
724 | 最后是实践理论相结合层面,主要就是把我一些新学习的没实践过的理论付诸实践。就比如学习了JVM调优和问题排查,就自己写一个死锁程序或者OOM程序然后用排查工具来排查一下问题。
725 |
726 |
--------------------------------------------------------------------------------