Skip to content

Commit 75801c5

Browse files
committed
update
1 parent e44546f commit 75801c5

13 files changed

+248
-151
lines changed

Java/Java基础面试题.md

Lines changed: 113 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -95,15 +95,53 @@
9595

9696
黑白双方负责接受用户的输入,并告知棋盘系统棋子布局发生变化,棋盘系统接收到了棋子的变化的信息就负责在屏幕上面显示出这种变化,同时利用规则系统来对棋局进行判定。
9797

98-
## JKD和JRE的区别?
98+
## JKD/JRE/JVM三者的关系
9999

100-
JDK和JRE是Java开发和运行工具,其中JDK包含了JRE,而JRE是可以独立安装的。
100+
### JVM
101101

102-
**JDK**Java Development Kit,JAVA语言的软件工具开发包,是整个JAVA开发的核心,它包含了JAVA的运行(JVM+JAVA类库)环境和JAVA工具
102+
**JVM** :英文名称(Java Virtual Machine),就是我们耳熟能详的 Java 虚拟机。Java 能够跨平台运行的核心在于 JVM
103103

104-
**JRE**:Java Runtime Environment,Java运行环境,包含JVM标准实现及Java核心类库。JRE是Java运行环境,并不是一个开发环境,所以没有包含任何开发工具(如编译器和调试器)。
104+
![](https://raw.githubusercontent.com/Tyson0314/img/master/20220402230447.png)
105105

106-
JRE是运行基于Java语言编写的程序所不可缺少的运行环境。也是通过它,Java的开发者才得以将自己开发的程序发布到用户手中,让用户使用。
106+
所有的java程序会首先被编译为.class的类文件,这种类文件可以在虚拟机上执行。也就是说class文件并不直接与机器的操作系统交互,而是经过虚拟机间接与操作系统交互,由虚拟机将程序解释给本地系统执行。
107+
108+
针对不同的系统有不同的 jvm 实现,有 Linux 版本的 jvm 实现,也有Windows 版本的 jvm 实现,但是同一段代码在编译后的字节码是一样的。这就是Java能够跨平台,实现一次编写,多处运行的原因所在。
109+
110+
### JRE
111+
112+
英文名称(Java Runtime Environment),就是Java 运行时环境。我们编写的Java程序必须要在JRE才能运行。它主要包含两个部分,JVM 和 Java 核心类库。
113+
114+
![](https://raw.githubusercontent.com/Tyson0314/img/master/20220401234008.png)
115+
116+
JRE是Java的运行环境,并不是一个开发环境,所以没有包含任何开发工具,如编译器和调试器等。
117+
118+
如果你只是想运行Java程序,而不是开发Java程序的话,那么你只需要安装JRE即可。
119+
120+
### JDK
121+
122+
英文名称(Java Development Kit),就是 Java 开发工具包
123+
124+
学过Java的同学,都应该安装过JDK。当我们安装完JDK之后,目录结构是这样的
125+
126+
![](https://cdn.jsdelivr.net/gh/Tyson0314/img/20220404120509.png)
127+
128+
可以看到,JDK目录下有个JRE,也就是JDK中已经集成了 JRE,不用单独安装JRE。
129+
130+
另外,JDK中还有一些好用的工具,如jinfo,jps,jstack等。
131+
132+
![](https://cdn.jsdelivr.net/gh/Tyson0314/img/20220404120507.png)
133+
134+
135+
136+
### 总结
137+
138+
最后,总结一下JDK/JRE/JVM,他们三者的关系
139+
140+
JRE = JVM + Java 核心类库
141+
142+
JDK = JRE + Java工具 + 编译器 + 调试器
143+
144+
![](https://raw.githubusercontent.com/Tyson0314/img/master/20220402230613.png)
107145

108146
## 面向对象有哪些特性?
109147

@@ -944,6 +982,76 @@ unchecked Exception:
944982

945983
- **throws**:用在方法签名中,用于声明该方法可能抛出的异常。子类方法抛出的异常范围更加小,或者根本不抛异常。
946984

985+
## 通过故事讲清楚NIO
986+
987+
下面通过一个例子来讲解下。
988+
989+
假设某银行只有10个职员。该银行的业务流程分为以下4个步骤:
990+
991+
1) 顾客填申请表(5分钟);
992+
993+
2) 职员审核(1分钟);
994+
995+
3) 职员叫保安去金库取钱(3分钟);
996+
997+
4) 职员打印票据,并将钱和票据返回给顾客(1分钟)。
998+
999+
下面我们看看银行不同的工作方式对其工作效率到底有何影响。
1000+
1001+
首先是BIO方式。
1002+
1003+
每来一个顾客,马上由一位职员来接待处理,并且这个职员需要负责以上4个完整流程。当超过10个顾客时,剩余的顾客需要排队等候。
1004+
1005+
一个职员处理一个顾客需要10分钟(5+1+3+1)时间。一个小时(60分钟)能处理6个顾客,一共10个职员,那就是只能处理60个顾客。
1006+
1007+
可以看到银行职员的工作状态并不饱和,比如在第1步,其实是处于等待中。
1008+
1009+
这种工作其实就是BIO,每次来一个请求(顾客),就分配到线程池中由一个线程(职员)处理,如果超出了线程池的最大上限(10个),就扔到队列等待 。
1010+
1011+
那么如何提高银行的吞吐量呢?
1012+
1013+
思路就是:**分而治之**,将任务拆分开来,由专门的人负责专门的任务。
1014+
1015+
具体来讲,银行专门指派一名职员A,A的工作就是每当有顾客到银行,他就递上表格让顾客填写。每当有顾客填好表后,A就将其随机指派给剩余的9名职员完成后续步骤。
1016+
1017+
这种方式下,假设顾客非常多,职员A的工作处于饱和中,他不断的将填好表的顾客带到柜台处理。
1018+
1019+
柜台一个职员5分钟能处理完一个顾客,一个小时9名职员能处理:9*(60/5)=108。
1020+
1021+
可见工作方式的转变能带来效率的极大提升。
1022+
1023+
这种工作方式其实就NIO的思路。
1024+
1025+
下图是非常经典的NIO说明图,`mainReactor`线程负责监听server socket,接收新连接,并将建立的socket分派给`subReactor`
1026+
1027+
`subReactor`可以是一个线程,也可以是线程池,负责多路分离已连接的socket,读写网络数据。这里的读写网络数据可类比顾客填表这一耗时动作,对具体的业务处理功能,其扔给worker线程池完成
1028+
1029+
可以看到典型NIO有三类线程,分别是`mainReactor`线程、`subReactor`线程、`work`线程。
1030+
1031+
不同的线程干专业的事情,最终每个线程都没空着,系统的吞吐量自然就上去了。
1032+
1033+
![](https://gitee.com/tysondai/img/raw/master/20220423154450.png)
1034+
1035+
1036+
1037+
**那这个流程还有没有什么可以提高的地方呢?**
1038+
1039+
可以看到,在这个业务流程里边第3个步骤,职员叫保安去金库取钱(3分钟)。这3分钟柜台职员是在等待中度过的,可以把这3分钟利用起来。
1040+
1041+
还是分而治之的思路,指派1个职员B来专门负责第3步骤。
1042+
1043+
每当柜台员工完成第2步时,就通知职员B来负责与保安沟通取钱。这时候柜台员工可以继续处理下一个顾客。
1044+
1045+
当职员B拿到钱之后,通知顾客钱已经到柜台了,让顾客重新排队处理,当柜台职员再次服务该顾客时,发现该顾客前3步已经完成,直接执行第4步即可。
1046+
1047+
在当今web服务中,经常需要通过RPC或者Http等方式调用第三方服务,这里对应的就是第3步,如果这步耗时较长,通过异步方式将能极大降低资源使用率。
1048+
1049+
NIO+异步的方式能让少量的线程做大量的事情。这适用于很多应用场景,比如代理服务、api服务、长连接服务等等。这些应用如果用同步方式将耗费大量机器资源。
1050+
1051+
不过虽然NIO+异步能提高系统吞吐量,但其并不能让一个请求的等待时间下降,相反可能会增加等待时间。
1052+
1053+
最后,NIO基本思想总结起来就是:**分而治之,将任务拆分开来,由专门的人负责专门的任务**
1054+
9471055
## BIO/NIO/AIO区别的区别?
9481056

9491057
**同步阻塞IO** : 用户进程发起一个IO操作以后,必须等待IO操作的真正完成后,才能继续运行。

Java/Java集合面试题.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ while(itr.hasNext()) {
141141

142142
## Arraylist 和 Vector 的区别
143143

144-
1. ArrayList在内存不够时默认是扩展50% + 1个,Vector是默认扩展1倍
144+
1. ArrayList在内存不够时扩容为原来的1.5倍,Vector是扩容为原来的2倍
145145
2. Vector属于线程安全级别的,但是大多数情况下不使用Vector,因为操作Vector效率比较低。
146146

147147
## Arraylist 与 LinkedList的区别
@@ -422,7 +422,7 @@ put 操作流程:
422422

423423
### CopyOnWrite
424424

425-
写时复制。当我们往容器添加元素时,不直接往容器添加,而是先将当前容器进行复制,复制出一个新的容器,然后往新的容器添加元素,添加完元素之后,再将原容器的引用指向新容器。这样做的好处就是可以对`CopyOnWrite`容器进行并发的读而不需要加锁,因为当前容器不会被修改。
425+
Copy-On-Write,写时复制。当我们往容器添加元素时,不直接往容器添加,而是先将当前容器进行复制,复制出一个新的容器,然后往新的容器添加元素,添加完元素之后,再将原容器的引用指向新容器。这样做的好处就是可以对`CopyOnWrite`容器进行并发的读而不需要加锁,因为当前容器不会被修改。
426426

427427
```java
428428
public boolean add(E e) {
@@ -443,13 +443,19 @@ put 操作流程:
443443

444444
从JDK1.5开始Java并发包里提供了两个使用CopyOnWrite机制实现的并发容器,它们是`CopyOnWriteArrayList``CopyOnWriteArraySet`
445445

446-
`CopyOnWriteArrayList`中add方法添加的时候是需要加锁的,保证同步,避免了多线程写的时候复制出多个副本。读的时候不需要加锁,如果读的时候有其他线程正在向`CopyOnWriteArrayList`添加数据,还是可以读到旧的数据。
447-
448446
**缺点:**
449447

450448
- 内存占用问题。由于CopyOnWrite的写时复制机制,在进行写操作的时候,内存里会同时驻扎两个对象的内存。
451449
- CopyOnWrite容器不能保证数据的实时一致性,可能读取到旧数据。
452450

451+
### CopyOnWriteArrayList
452+
453+
CopyOnWriteArrayList相当于线程安全的ArrayList,CopyOnWriteArrayList使用了一种叫写时复制的方法,当有新元素add到CopyOnWriteArrayList时,先从原有的数组中拷贝一份出来,然后在新的数组做写操作,写完之后,再将原来的数组引用指向到新数组。
454+
455+
`CopyOnWriteArrayList`中add方法添加的时候是需要加锁的,保证同步,避免了多线程写的时候复制出多个副本。读的时候不需要加锁,如果读的时候有其他线程正在向`CopyOnWriteArrayList`添加数据,还是可以读到旧的数据。
456+
457+
CopyOnWrite并发容器用于读多写少的并发场景。
458+
453459
### ConcurrentLinkedQueue
454460

455461
非阻塞队列。高效的并发队列,使用链表实现。可以看做一个线程安全的 `LinkedList`,通过 CAS 操作实现。

中间件/Redis面试题.md

Lines changed: 10 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,6 @@
3535

3636
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
3737

38-
> 首先给大家分享一个github仓库,上面放了**200多本经典的计算机书籍**,包括C语言、C++、Java、Python、前端、数据库、操作系统、计算机网络、数据结构和算法、机器学习、编程人生等,可以star一下,下次找书直接在上面搜索,仓库持续更新中~
39-
>
40-
> github地址:https://github.com/Tyson0314/java-books
41-
>
42-
> 如果github访问不了,可以访问gitee仓库。
43-
>
44-
> gitee地址:https://gitee.com/tysondai/java-books
45-
4638
## Redis是什么?
4739

4840
Redis(`Remote Dictionary Server`)是一个使用 C 语言编写的,高性能非关系型的键值对数据库。与传统数据库不同的是,Redis 的数据是存在内存中的,所以读写速度非常快,被广泛应用于缓存方向。Redis可以将数据写入磁盘中,保证了数据的安全不丢失,而且Redis的操作是原子性的。
@@ -52,11 +44,11 @@ Redis(`Remote Dictionary Server`)是一个使用 C 语言编写的,高性
5244
**优点**
5345

5446
1. **基于内存操作**,内存读写速度快。
55-
2. Redis是**单线程**的,避免线程切换开销及多线程的竞争问题。单线程是指网络请求使用一个线程来处理,即一个线程处理所有网络请求,Redis 运行时不止有一个线程,比如数据持久化的过程会另起线程。
56-
3. **支持多种数据类型**,包括String、Hash、List、Set、ZSet等。
57-
4. **支持持久化**。Redis支持RDB和AOF两种持久化机制,持久化功能可以有效地避免数据丢失问题。
58-
5. **支持事务**。Redis的所有操作都是原子性的,同时Redis还支持对几个操作合并后的原子性执行。
47+
2. **支持多种数据类型**,包括String、Hash、List、Set、ZSet等。
48+
3. **支持持久化**。Redis支持RDB和AOF两种持久化机制,持久化功能可以有效地避免数据丢失问题。
49+
4. **支持事务**。Redis的所有操作都是原子性的,同时Redis还支持对几个操作合并后的原子性执行。
5950
6. **支持主从复制**。主节点会自动将数据同步到从节点,可以进行读写分离。
51+
6. Redis命令的处理是**单线程**的。Redis6.0引入了多线程,需要注意的是,**多线程用于处理网络数据的读写和协议解析**,Redis命令执行还是单线程的。
6052

6153
**缺点**
6254

@@ -67,10 +59,7 @@ Redis(`Remote Dictionary Server`)是一个使用 C 语言编写的,高性
6759
## Redis为什么这么快?
6860

6961
- **基于内存**:Redis是使用内存存储,没有磁盘IO上的开销。数据存在内存中,读写速度快。
70-
- **单线程实现**( Redis 6.0以前):Redis使用单个线程处理请求,避免了多个线程之间线程切换和锁资源争用的开销。
71-
7262
- **IO多路复用模型**:Redis 采用 IO 多路复用技术。Redis 使用单线程来轮询描述符,将数据库的操作都转换成了事件,不在网络I/O上浪费过多的时间。
73-
7463
- **高效的数据结构**:Redis 每种数据类型底层都做了优化,目的就是为了追求更快的速度。
7564

7665
## 讲讲Redis的线程模型?
@@ -82,20 +71,6 @@ Redis基于Reactor模式开发了网络事件处理器,这个处理器被称
8271

8372
虽然文件事件处理器以单线程方式运行, 但通过使用 I/O 多路复用程序来监听多个套接字, 文件事件处理器既实现了高性能的网络通信模型, 又可以很好地与 redis 服务器中其他同样以单线程方式运行的模块进行对接, 这保持了 Redis 内部单线程设计的简单性。
8473

85-
## Redis为何选择单线程模型?
86-
87-
* 避免过多的**上下文切换开销**。程序始终运行在进程中单个线程内,没有多线程切换的场景。
88-
* **避免同步机制的开销**:如果 Redis选择多线程模型,需要考虑数据同步的问题,则必然会引入某些同步机制,会导致在操作数据过程中带来更多的开销,增加程序复杂度的同时还会降低性能。
89-
* **实现简单,方便维护**:如果 Redis使用多线程模式,那么所有的底层数据结构的设计都必须考虑线程安全问题,那么 Redis 的实现将会变得更加复杂。
90-
91-
## Redis6.0为何引入多线程?
92-
93-
Redis支持多线程主要有两个原因:
94-
95-
* 可以充分利用服务器 CPU 资源,单线程模型的主线程只能利用一个cpu;
96-
97-
* 多线程任务可以分摊 Redis 同步 IO 读写的负荷。
98-
9974
## Redis应用场景有哪些?
10075

10176
1. **缓存热点数据**,缓解数据库的压力。
@@ -106,12 +81,12 @@ Redis支持多线程主要有两个原因:
10681

10782
## Memcached和Redis的区别?
10883

109-
1. Redis 只使用**单核**,而 Memcached 可以使用多核
110-
2. MemCached 数据结构单一,仅用来缓存数据,而 **Redis 支持多种数据类型**
111-
3. MemCached 不支持数据持久化,重启后数据会消失。**Redis 支持数据持久化**
112-
4. **Redis 提供主从同步机制和 cluster 集群部署能力**,能够提供高可用服务。Memcached 没有提供原生的集群模式,需要依靠客户端实现往集群中分片写入数据
113-
5. Redis 的速度比 Memcached 快很多。
114-
6. Redis 使用**单线程的多路 IO 复用模型**,Memcached使用多线程的非阻塞 IO 模型
84+
1. MemCached 数据结构单一,仅用来缓存数据,而 **Redis 支持多种数据类型**
85+
2. MemCached 不支持数据持久化,重启后数据会消失。**Redis 支持数据持久化**
86+
3. **Redis 提供主从同步机制和 cluster 集群部署能力**,能够提供高可用服务。Memcached 没有提供原生的集群模式,需要依靠客户端实现往集群中分片写入数据
87+
4. Redis 的速度比 Memcached 快很多
88+
5. Redis 使用**单线程的多路 IO 复用模型**,Memcached使用多线程的非阻塞 IO 模型。(Redis6.0引入了多线程IO,**用来处理网络数据的读写和协议解析**,但是命令的执行仍然是单线程)
89+
6. value 值大小不同:Redis 最大可以达到 512M;memcache 只有 1mb
11590

11691
## 为什么要用 Redis 而不用 map/guava 做缓存?
11792

中间件/消息队列面试题.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,7 @@
2525

2626
## RabbitMQ如何保证消息的顺序性?
2727

28-
当生产者只有1个,消费者有多个,每个consumer依次从mq中拿数据取数据消费,这种时候,即使mq中的消息不乱序,但由于消费者处理消息的速度不同,最终产生结果的顺序会不同(比如消费者逻辑是插入数据到数据库)
29-
30-
解决办法: 保证顺序的消息都放到1个queue里,同时只被1个消费者消费。
28+
单线程消费保证消息的顺序性;对消息进行编号,消费者根据编号处理消息。
3129

3230
## 如何避免消息重复消费?
3331

其他/设计模式.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,15 @@
2222

2323
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
2424

25+
## 设计模式的六大原则
26+
27+
- 开闭原则:对扩展开放,对修改关闭,多使用抽象类和接口。
28+
- 里氏替换原则:基类可以被子类替换,使用抽象类继承,不使用具体类继承。
29+
- 依赖倒转原则:要依赖于抽象,不要依赖于具体,针对接口编程,不针对实现编程。
30+
- 接口隔离原则:使用多个隔离的接口,比使用单个接口好,建立最小的接口。
31+
- 迪米特法则:一个软件实体应当尽可能少地与其他实体发生相互作用,通过中间类建立联系。
32+
- 合成复用原则:尽量使用合成/聚合,而不是使用继承。
33+
2534
## 单例模式
2635

2736
需要对实例字段使用线程安全的延迟初始化,使用双重检查锁定的方案;需要对静态字段使用线程安全的延迟初始化,使用静态内部类的方案。

操作系统/操作系统面试题.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,19 @@
3838
## 线程和协程有什么区别呢?
3939

4040
1、线程是抢占式,而协程是非抢占式的,所以需要用户自己释放使用权来切换到其他协程,因此同一时间其实只有一个协程拥有运行权,相当于单线程的能力。
41-
2、线程是协程的资源。协程通过 可以关联任意线程或线程池的执行器(Interceptor)来间接使用线程的资源的。
41+
2、线程是协程的资源。协程通过 可以关联任意线程或线程池的执行器(Interceptor)来间接使用线程的资源的。
42+
43+
## 进程通信
44+
45+
进程间通信方式有以下几种:
46+
47+
1、**管道通信**
48+
49+
匿名管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
50+
有名管道是半双工的通信方式,数据只能单向流动。
51+
52+
2、**消息队列**
53+
54+
3、**共享内存**。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。
55+
56+
4、**信号量**。信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。

0 commit comments

Comments
 (0)