auto commit
This commit is contained in:
+6
-5
@@ -30,15 +30,15 @@ Docker 主要解决环境配置问题,它是一种虚拟化技术,对进程
|
||||
|
||||
## 启动速度
|
||||
|
||||
启动虚拟机需要启动虚拟机的操作系统,再启动相应的应用,这个过程会非常慢;
|
||||
启动虚拟机需要启动虚拟机的操作系统,再启动应用,这个过程非常慢;
|
||||
|
||||
而启动 Docker 相当于启动宿主操作系统上的一个进程。
|
||||
|
||||
## 占用资源
|
||||
|
||||
虚拟机是一个完整的操作系统,需要占用大量的磁盘空间、内存和 CPU,一台机器只能开启几十个的虚拟机。
|
||||
虚拟机是一个完整的操作系统,需要占用大量的磁盘、内存和 CPU,一台机器只能开启几十个的虚拟机。
|
||||
|
||||
而 Docker 只是一个进程,只需要将应用以及相应的组件打包,在运行时占用很少的资源,一台机器可以开启成千上万个 Docker。
|
||||
而 Docker 只是一个进程,只需要将应用以及相关的组件打包,在运行时占用很少的资源,一台机器可以开启成千上万个 Docker。
|
||||
|
||||
参考资料:
|
||||
|
||||
@@ -58,7 +58,7 @@ Docker 使用分层技术和镜像,使得应用可以更容易复用重复部
|
||||
|
||||
## 更容易扩展
|
||||
|
||||
可以使用基础镜像进一步扩展得到新的镜像,并且官方和开源社区提供了大量的镜像,通过扩展这些镜像得到我们想要的镜像非常容易。
|
||||
可以使用基础镜像进一步扩展得到新的镜像,并且官方和开源社区提供了大量的镜像,通过扩展这些镜像可以非常容易得到我们想要的镜像。
|
||||
|
||||
参考资料:
|
||||
|
||||
@@ -91,7 +91,7 @@ Docker 轻量级的特点使得它很适合用于部署、维护、组合微服
|
||||
|
||||
镜像包含着容器运行时所需要的代码以及其它组件,它是一种分层结构,每一层都是只读的(read-only layers)。构建镜像时,会一层一层构建,前一层是后一层的基础。镜像的这种分层存储结构很适合镜像的复用以及定制。
|
||||
|
||||
在构建容器时,通过在镜像的基础上添加一个可写层(writable layer),用来保存着容器运行过程中的修改。
|
||||
构建容器时,通过在镜像的基础上添加一个可写层(writable layer),用来保存着容器运行过程中的修改。
|
||||
|
||||
<div align="center"> <img src="../pics//docker-filesystems-busyboxrw.png"/> </div><br>
|
||||
|
||||
@@ -100,3 +100,4 @@ Docker 轻量级的特点使得它很适合用于部署、维护、组合微服
|
||||
- [How to Create Docker Container using Dockerfile](https://linoxide.com/linux-how-to/dockerfile-create-docker-container/)
|
||||
- [理解 Docker(2):Docker 镜像](http://www.cnblogs.com/sammyliu/p/5877964.html)
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -707,7 +707,7 @@ public class EqualExample {
|
||||
|
||||
## hashCode()
|
||||
|
||||
hasCode() 返回散列值,而 equals() 是用来判断两个对象是否等价。等价的两个对象散列值一定相同,但是散列值相同的两个对象不一定等价。
|
||||
hashCode() 返回散列值,而 equals() 是用来判断两个对象是否等价。等价的两个对象散列值一定相同,但是散列值相同的两个对象不一定等价。
|
||||
|
||||
在覆盖 equals() 方法时应当总是覆盖 hashCode() 方法,保证等价的两个对象散列值也相等。
|
||||
|
||||
|
||||
+1
-1
@@ -742,7 +742,7 @@ new capacity : 00100000
|
||||
|
||||
对于一个 Key,
|
||||
|
||||
- 它的哈希值如果在第 6 位上为 0,那么取模得到的结果和之前一样;
|
||||
- 它的哈希值如果在第 5 位上为 0,那么取模得到的结果和之前一样;
|
||||
- 如果为 1,那么得到的结果为原来的结果 +16。
|
||||
|
||||
### 7. 扩容-计算数组容量
|
||||
|
||||
+3
-1
@@ -168,6 +168,8 @@ public static void main(String[] args) throws ExecutionException, InterruptedExc
|
||||
|
||||
同样也是需要实现 run() 方法,因为 Thread 类也实现了 Runable 接口。
|
||||
|
||||
当调用 start() 方法启动一个线程时,虚拟机会将该线程放入就绪队列中等待被调度,当一个线程被调度时会执行该线程的 run() 方法。
|
||||
|
||||
```java
|
||||
public class MyThread extends Thread {
|
||||
public void run() {
|
||||
@@ -1018,7 +1020,7 @@ ForkJoinPool 实现了工作窃取算法来提高 CPU 的利用率。每个线
|
||||
|
||||
如果多个线程对同一个共享数据进行访问而不采取同步操作的话,那么操作的结果是不一致的。
|
||||
|
||||
以下代码演示了 1000 个线程同时对 cnt 执行自增操作,操作结束之后它的值为 997 而不是 1000。
|
||||
以下代码演示了 1000 个线程同时对 cnt 执行自增操作,操作结束之后它的值有可能小于 1000。
|
||||
|
||||
```java
|
||||
public class ThreadUnsafeExample {
|
||||
|
||||
+1
-1
@@ -92,7 +92,7 @@ java -Xms1M -Xmx2M HackTheJava
|
||||
|
||||
对这块区域进行垃圾回收的主要目标是对常量池的回收和对类的卸载,但是一般比较难实现。
|
||||
|
||||
JDK 1.7 之前,HotSpot 虚拟机把它当成永久代来进行垃圾回收。但是从 JDK 1.7 开始,已经把原本放在永久代的字符串常量池移到 Native Method 中。
|
||||
HotSpot 虚拟机把它当成永久代来进行垃圾回收。但是很难确定永久代的大小,因为它受到很多因素影响,并且每次 Full GC 之后永久代的大小都会改变,所以经常会抛出 OutOfMemoryError 异常。为了更容易管理方法区,从 JDK 1.8 开始,移除永久代,并把方法区移至元空间,它位于本地内存中,而不是虚拟机内存中。
|
||||
|
||||
## 运行时常量池
|
||||
|
||||
|
||||
+89
-100
File diff suppressed because it is too large
Load Diff
+13
-13
@@ -129,15 +129,16 @@ info 与 man 类似,但是 info 将文档分成一个个页面,每个页面
|
||||
/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/dmtsai/.local/bin:/home/dmtsai/bin
|
||||
```
|
||||
|
||||
env 命令可以获取当前终端的环境变量。
|
||||
|
||||
## sudo
|
||||
|
||||
sudo 允许一般用户使用 root 可执行的命令,不过只有在 /etc/sudoers 配置文件中添加的用户才能使用该指令。
|
||||
|
||||
## 包管理工具
|
||||
|
||||
RPM 和 DPKG 为最常见的两类软件包管理工具。RPM 全称为 Redhat Package Manager,最早由 Red Hat 公司制定实施,随后被 GNU 开源操作系统接受并成为很多 Linux 系统 (RHEL) 的既定软件标准。与 RPM 进行竞争的是基于 Debian 操作系统 (UBUNTU) 的 DEB 软件包管理工具 DPKG,全称为 Debian Package,功能方面与 RPM 相似。
|
||||
RPM 和 DPKG 为最常见的两类软件包管理工具:
|
||||
|
||||
- RPM 全称为 Redhat Package Manager,最早由 Red Hat 公司制定实施,随后被 GNU 开源操作系统接受并成为很多 Linux 系统 (RHEL) 的既定软件标准。
|
||||
- 与 RPM 进行竞争的是基于 Debian 操作系统 (Ubuntu) 的 DEB 软件包管理工具 DPKG,全称为 Debian Package,功能方面与 RPM 相似。
|
||||
|
||||
YUM 基于 RPM,具有依赖管理功能,并具有软件升级的功能。
|
||||
|
||||
@@ -194,13 +195,13 @@ IDE(ATA)全称 Advanced Technology Attachment,接口速度最大为 133MB/
|
||||
|
||||
### 2. SATA
|
||||
|
||||
SATA 全称 Serial ATA,也就是使用串口的 ATA 接口,因抗干扰性强,且对数据线的长度要求比 ATA 低很多,支持热插拔等功能,SATA-II 的接口速度为 300MiB/s,而新的 SATA-III 标准可达到 600MiB/s 的传输速度。SATA 的数据线也比 ATA 的细得多,有利于机箱内的空气流通,整理线材也比较方便。
|
||||
SATA 全称 Serial ATA,也就是使用串口的 ATA 接口,抗干扰性强,且对数据线的长度要求比 ATA 低很多,支持热插拔等功能。SATA-II 的接口速度为 300MiB/s,而新的 SATA-III 标准可达到 600MiB/s 的传输速度。SATA 的数据线也比 ATA 的细得多,有利于机箱内的空气流通,整理线材也比较方便。
|
||||
|
||||
<div align="center"> <img src="../pics//f9f2a16b-4843-44d1-9759-c745772e9bcf.jpg" width=""/> </div><br>
|
||||
|
||||
### 3. SCSI
|
||||
|
||||
SCSI 全称是 Small Computer System Interface(小型机系统接口),经历多代的发展,从早期的 SCSI-II,到目前的 Ultra320 SCSI 以及 Fiber-Channel(光纤通道),接口型式也多种多样。SCSI 硬盘广为工作站级个人电脑以及服务器所使用,因此会使用较为先进的技术,如碟片转速 15000rpm 的高转速,且资料传输时 CPU 占用率较低,但是单价也比相同容量的 ATA 及 SATA 硬盘更加昂贵。
|
||||
SCSI 全称是 Small Computer System Interface(小型机系统接口),经历多代的发展,从早期的 SCSI-II 到目前的 Ultra320 SCSI 以及 Fiber-Channel(光纤通道),接口型式也多种多样。SCSI 硬盘广为工作站级个人电脑以及服务器所使用,因此会使用较为先进的技术,如碟片转速 15000rpm 的高转速,且传输时 CPU 占用率较低,但是单价也比相同容量的 ATA 及 SATA 硬盘更加昂贵。
|
||||
|
||||
<div align="center"> <img src="../pics//f0574025-c514-49f5-a591-6d6a71f271f7.jpg" width=""/> </div><br>
|
||||
|
||||
@@ -229,7 +230,7 @@ Linux 中每个硬件都被当做一个文件,包括磁盘。磁盘以磁盘
|
||||
|
||||
MBR 中,第一个扇区最重要,里面有主要开机记录(Master boot record, MBR)及分区表(partition table),其中主要开机记录占 446 bytes,分区表占 64 bytes。
|
||||
|
||||
分区表只有 64 bytes,最多只能存储 4 个分区,这 4 个分区为主分区(Primary)和扩展分区(Extended)。其中扩展分区只有一个,它将其它扇区用来记录分区表,因此通过扩展分区可以分出更多分区,这些分区称为逻辑分区。
|
||||
分区表只有 64 bytes,最多只能存储 4 个分区,这 4 个分区为主分区(Primary)和扩展分区(Extended)。其中扩展分区只有一个,它使用其它扇区用记录额外的分区表,因此通过扩展分区可以分出更多分区,这些分区称为逻辑分区。
|
||||
|
||||
Linux 也把分区当成文件,分区文件的命名方式为:磁盘文件名 + 编号,例如 /dev/sda1。注意,逻辑分区的编号从 5 开始。
|
||||
|
||||
@@ -251,10 +252,10 @@ MBR 不支持 2.2 TB 以上的硬盘,GPT 则最多支持到 2<sup>33</sup> TB
|
||||
|
||||
BIOS(Basic Input/Output System,基本输入输出系统),它是一个固件(嵌入在硬件中的软件),BIOS 程序存放在断电后内容不会丢失的只读内存中。
|
||||
|
||||
BIOS 是开机的时候计算机执行的第一个程序,这个程序知道可以开机的磁盘,并读取磁盘第一个扇区的主要开机记录(MBR),由主要开机记录(MBR)执行其中的开机管理程序,这个开机管理程序会加载操作系统的核心文件。
|
||||
|
||||
<div align="center"> <img src="../pics//50831a6f-2777-46ea-a571-29f23c85cc21.jpg"/> </div><br>
|
||||
|
||||
BIOS 是开机的时候计算机执行的第一个程序,这个程序知道可以开机的磁盘,并读取磁盘第一个扇区的主要开机记录(MBR),由主要开机记录(MBR)执行其中的开机管理程序,这个开机管理程序会加载操作系统的核心文件。
|
||||
|
||||
主要开机记录(MBR)中的开机管理程序提供以下功能:选单、载入核心文件以及转交其它开机管理程序。转交这个功能可以用来实现了多重引导,只需要将另一个操作系统的开机管理程序安装在其它分区的启动扇区上,在启动开机管理程序时,就可以通过选单选择启动当前的操作系统或者转交给其它开机管理程序从而启动另一个操作系统。
|
||||
|
||||
下图中,第一扇区的主要开机记录(MBR)中的开机管理程序提供了两个选单:M1、M2,M1 指向了 Windows 操作系统,而 M2 指向其它分区的启动扇区,里面包含了另外一个开机管理程序,提供了一个指向 Linux 的选单。
|
||||
@@ -283,11 +284,10 @@ BIOS 不可以读取 GPT 分区表,而 UEFI 可以。
|
||||
除此之外还包括:
|
||||
|
||||
- superblock:记录文件系统的整体信息,包括 inode 和 block 的总量、使用量、剩余量,以及文件系统的格式与相关信息等;
|
||||
- block bitmap:记录 block 是否被使用的位域;
|
||||
- block bitmap:记录 block 是否被使用的位域。
|
||||
|
||||
<div align="center"> <img src="../pics//BSD_disk.png" width="800"/> </div><br>
|
||||
|
||||
|
||||
## 文件读取
|
||||
|
||||
对于 Ext2 文件系统,当要读取一个文件的内容时,先在 inode 中去查找文件内容所在的所有 block,然后把所有 block 的内容读出来。
|
||||
@@ -1169,7 +1169,7 @@ dmtsai lines: 5 columns: 9
|
||||
| S | interruptible sleep (waiting for an event to complete) |
|
||||
| Z | zombie (terminated but not reaped by its parent) |
|
||||
| T | stopped (either by a job control signal or because it is being traced) |
|
||||
|
||||
<br>
|
||||
<div align="center"> <img src="../pics//76a49594323247f21c9b3a69945445ee.png" width=""/> </div><br>
|
||||
|
||||
## SIGCHLD
|
||||
@@ -1179,12 +1179,12 @@ dmtsai lines: 5 columns: 9
|
||||
- 得到 SIGCHLD 信号;
|
||||
- waitpid() 或者 wait() 调用会返回。
|
||||
|
||||
<div align="center"> <img src="../pics//flow.png" width=""/> </div><br>
|
||||
|
||||
其中子进程发送的 SIGCHLD 信号包含了子进程的信息,包含了进程 ID、进程状态、进程使用 CPU 的时间等。
|
||||
|
||||
在子进程退出时,它的进程描述符不会立即释放,这是为了让父进程得到子进程信息,父进程通过 wait() 和 waitpid() 来获得一个已经退出的子进程的信息。
|
||||
|
||||
<div align="center"> <img src="../pics//flow.png" width=""/> </div><br>
|
||||
|
||||
## wait()
|
||||
|
||||
```c
|
||||
|
||||
+1
-1
@@ -549,7 +549,7 @@ def main():
|
||||
|
||||
# 十二、Sentinel
|
||||
|
||||
Sentinel(哨兵)可以监听主服务器,并在主服务器进入下线状态时,自动从从服务器中选举出新的主服务器。
|
||||
Sentinel(哨兵)可以监听集群中的服务器,并在主服务器进入下线状态时,自动从从服务器中选举出新的主服务器。
|
||||
|
||||
# 十三、分片
|
||||
|
||||
|
||||
+44
-61
File diff suppressed because it is too large
Load Diff
+10
-4
@@ -1082,7 +1082,7 @@ false
|
||||
|
||||
```java
|
||||
public boolean isNumeric(char[] str) {
|
||||
if (str == null)
|
||||
if (str == null || str.length == 0)
|
||||
return false;
|
||||
return new String(str).matches("[+-]?\\d*(\\.\\d+)?([eE][+-]?\\d+)?");
|
||||
}
|
||||
@@ -2779,19 +2779,25 @@ public List<Map.Entry<Integer, Double>> dicesSum(int n) {
|
||||
|
||||
```java
|
||||
public boolean isContinuous(int[] nums) {
|
||||
|
||||
if (nums.length < 5)
|
||||
return false;
|
||||
|
||||
Arrays.sort(nums);
|
||||
|
||||
// 统计癞子数量
|
||||
int cnt = 0;
|
||||
for (int num : nums) /* 统计癞子数量 */
|
||||
for (int num : nums)
|
||||
if (num == 0)
|
||||
cnt++;
|
||||
|
||||
// 使用癞子去补全不连续的顺子
|
||||
for (int i = cnt; i < nums.length - 1; i++) {
|
||||
if (nums[i + 1] == nums[i])
|
||||
return false;
|
||||
cnt -= nums[i + 1] - nums[i] - 1; /* 使用癞子去补全不连续的顺子 */
|
||||
cnt -= nums[i + 1] - nums[i] - 1;
|
||||
}
|
||||
|
||||
return cnt >= 0;
|
||||
}
|
||||
```
|
||||
@@ -2824,7 +2830,7 @@ public int LastRemaining_Solution(int n, int m) {
|
||||
|
||||
## 题目描述
|
||||
|
||||
可以有一次买入和一次卖出,买入必须在前。求最大收益。
|
||||
可以有一次买入和一次卖出,那么买入必须在前。求最大收益。
|
||||
|
||||
## 解题思路
|
||||
|
||||
|
||||
+1
-1
@@ -207,7 +207,7 @@ ResultSet rs = stmt.executeQuery();
|
||||
|
||||
拒绝服务攻击(denial-of-service attack,DoS),亦称洪水攻击,其目的在于使目标电脑的网络或系统资源耗尽,使服务暂时中断或停止,导致其正常用户无法访问。
|
||||
|
||||
分布式拒绝服务攻击(distributed denial-of-service attack,DDoS),指攻击者使用网络上两个或以上被攻陷的电脑作为“僵尸”向特定的目标发动“拒绝服务”式攻击。
|
||||
分布式拒绝服务攻击(distributed denial-of-service attack,DDoS),指攻击者使用两个或以上被攻陷的电脑作为“僵尸”向特定的目标发动“拒绝服务”式攻击。
|
||||
|
||||
# 参考资料
|
||||
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
|
||||
## 运行单元测试
|
||||
|
||||
不再需要在项目代码中添加测试代码,从而污染项目代码。
|
||||
不再需要在项目代码中添加测试代码,从而避免了污染项目代码。
|
||||
|
||||
## 将源代码转化为可执行文件
|
||||
|
||||
|
||||
+7
-6
@@ -9,6 +9,7 @@
|
||||
* [三、可靠性](#三可靠性)
|
||||
* [发送端的可靠性](#发送端的可靠性)
|
||||
* [接收端的可靠性](#接收端的可靠性)
|
||||
* [参考资料](#参考资料)
|
||||
<!-- GFM-TOC -->
|
||||
|
||||
|
||||
@@ -29,15 +30,10 @@
|
||||
发布与订阅模式和观察者模式有以下不同:
|
||||
|
||||
- 观察者模式中,观察者和主题都知道对方的存在;而在发布与订阅模式中,发布者与订阅者不知道对方的存在,它们之间通过频道进行通信。
|
||||
- 观察者模式是同步的,当事件触发时,主题会去调用观察者的方法,然后等待方法返回;而发布与订阅模式是异步的,发布者向频道发送一个消息之后,就不需要关心订阅者何时去订阅这个消息。
|
||||
- 观察者模式是同步的,当事件触发时,主题会调用观察者的方法,然后等待方法返回;而发布与订阅模式是异步的,发布者向频道发送一个消息之后,就不需要关心订阅者何时去订阅这个消息,可以立即返回。
|
||||
|
||||
<div align="center"> <img src="../pics//bee1ff1d-c80f-4b3c-b58c-7073a8896ab2.jpg"/> </div><br>
|
||||
|
||||
参考:
|
||||
|
||||
- [Observer vs Pub-Sub](http://developers-club.com/posts/270339/)
|
||||
- [消息队列中点对点与发布订阅区别](https://blog.csdn.net/lizhitao/article/details/47723105)
|
||||
|
||||
# 二、使用场景
|
||||
|
||||
## 异步处理
|
||||
@@ -78,3 +74,8 @@
|
||||
|
||||
- 保证接收端处理消息的业务逻辑具有幂等性:只要具有幂等性,那么消费多少次消息,最后处理的结果都是一样的。
|
||||
- 保证消息具有唯一编号,并使用一张日志表来记录已经消费的消息编号。
|
||||
|
||||
# 参考资料
|
||||
|
||||
- [Observer vs Pub-Sub](http://developers-club.com/posts/270339/)
|
||||
- [消息队列中点对点与发布订阅区别](https://blog.csdn.net/lizhitao/article/details/47723105)
|
||||
|
||||
+7
-7
@@ -23,7 +23,7 @@
|
||||
* [五、栈和队列](#五栈和队列)
|
||||
* [栈](#栈)
|
||||
* [队列](#队列)
|
||||
* [六、查找](#六查找)
|
||||
* [六、符号表](#六符号表)
|
||||
* [初级实现](#初级实现)
|
||||
* [二叉查找树](#二叉查找树)
|
||||
* [2-3 查找树](#2-3-查找树)
|
||||
@@ -507,7 +507,7 @@ public class QuickSort<T extends Comparable<T>> extends Sort<T> {
|
||||
|
||||
取 a[l] 作为切分元素,然后从数组的左端向右扫描直到找到第一个大于等于它的元素,再从数组的右端向左扫描找到第一个小于等于它的元素,交换这两个元素。不断进行这个过程,就可以保证左指针 i 的左侧元素都不大于切分元素,右指针 j 的右侧元素都不小于切分元素。当两个指针相遇时,将切分元素 a[l] 和 a[j] 交换位置。
|
||||
|
||||
<div align="center"> <img src="../pics//864bfa7d-1149-420c-a752-f9b3d4e782ec.png" width="400"/> </div><br>
|
||||
<div align="center"> <img src="../pics//766aedd0-1b00-4065-aa2b-7d31138df84f.png" width="400"/> </div><br>
|
||||
|
||||
```java
|
||||
private int partition(T[] nums, int l, int h) {
|
||||
@@ -770,7 +770,7 @@ public class HeapSort<T extends Comparable<T>> extends Sort<T> {
|
||||
|
||||
| 算法 | 稳定 | 时间复杂度 | 空间复杂度 | 备注 |
|
||||
| :---: | :---: |:---: | :---: | :---: |
|
||||
| 选择排序 | √| N<sup>2</sup> | 1 | |
|
||||
| 选择排序 | × | N<sup>2</sup> | 1 | |
|
||||
| 冒泡排序 | √ | N<sup>2</sup> | 1 | |
|
||||
| 插入排序 | √ | N \~ N<sup>2</sup> | 1 | 时间复杂度和初始顺序有关 |
|
||||
| 希尔排序 | × | N 的若干倍乘于递增序列的长度 | 1 | |
|
||||
@@ -1290,7 +1290,7 @@ public class ListQueue<Item> implements MyQueue<Item> {
|
||||
|
||||
|
||||
|
||||
# 六、查找
|
||||
# 六、符号表
|
||||
|
||||
符号表(Symbol Table)是一种存储键值对的数据结构,可以支持快速查找操作。
|
||||
|
||||
@@ -1411,9 +1411,9 @@ public class ListUnorderedST<Key, Value> implements UnorderedST<Key, Value> {
|
||||
|
||||
使用一对平行数组,一个存储键一个存储值。
|
||||
|
||||
rank() 方法至关重要,当键在表中时,它能够知道该键的位置;当键不在表中时,它也能知道在何处插入新键。
|
||||
二分查找的 rank() 方法至关重要,当键在表中时,它能够知道该键的位置;当键不在表中时,它也能知道在何处插入新键。
|
||||
|
||||
复杂度:二分查找最多需要 logN+1 次比较,使用二分查找实现的符号表的查找操作所需要的时间最多是对数级别的。但是插入操作需要移动数组元素,是线性级别的。
|
||||
二分查找最多需要 logN+1 次比较,使用二分查找实现的符号表的查找操作所需要的时间最多是对数级别的。但是插入操作需要移动数组元素,是线性级别的。
|
||||
|
||||
```java
|
||||
public class BinarySearchOrderedST<Key extends Comparable<Key>, Value> implements OrderedST<Key, Value> {
|
||||
@@ -2254,7 +2254,7 @@ from H1 to H3
|
||||
- c : 40
|
||||
- d : 80
|
||||
|
||||
可以将每种字符转换成二进制编码,例如将 a 转换为 00,b 转换为 01,c 转换为 10,d 转换为 11。这是最简单的一种编码方式,没有考虑各个字符的权值(出现频率)。而哈夫曼编码能让出现频率最高的字符的编码最短,从而保证整体的编码长度最短。
|
||||
可以将每种字符转换成二进制编码,例如将 a 转换为 00,b 转换为 01,c 转换为 10,d 转换为 11。这是最简单的一种编码方式,没有考虑各个字符的权值(出现频率)。而哈夫曼编码采用了贪心策略,使出现频率最高的字符的编码最短,从而保证整体的编码长度最短。
|
||||
|
||||
首先生成一颗哈夫曼树,每次生成过程中选取频率最少的两个节点,生成一个新节点作为它们的父节点,并且新节点的频率为两个节点的和。选取频率最少的原因是,生成过程使得先选取的节点在树的最底层,那么需要的编码长度更长,频率更少可以使得总编码长度更少。
|
||||
|
||||
|
||||
+4
-4
@@ -68,7 +68,7 @@
|
||||
|
||||
应用服务器只要不具有状态,那么就可以很容易地通过负载均衡器向集群中添加新的服务器。
|
||||
|
||||
关系型数据库的伸缩性通过 Sharding 来实现,将数据按一定的规则分布到不同的节点上,从而解决单台存储服务器存储空间限制。
|
||||
关系型数据库的伸缩性通过 Sharding 来实现,将数据按一定的规则分布到不同的节点上,从而解决单台存储服务器的存储空间限制。
|
||||
|
||||
对于非关系型数据库,它们天生就是为海量数据而诞生,对伸缩性的支持特别好。
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
|
||||
实现可扩展主要有两种方式:
|
||||
|
||||
- 使用消息队列进行解耦,应用之间通过消息传递的方式进行通信;
|
||||
- 使用消息队列进行解耦,应用之间通过消息传递进行通信;
|
||||
- 使用分布式服务将业务和可复用的服务分离开来,业务使用分布式服务框架调用可复用的服务。新增的产品可以通过调用可复用的服务来实现业务逻辑,对其它产品没有影响。
|
||||
|
||||
# 四、可用性
|
||||
@@ -87,7 +87,7 @@
|
||||
|
||||
保证高可用的主要手段是使用冗余,当某个服务器故障时就请求其它服务器。
|
||||
|
||||
应用服务器的冗余比较容易实现,只要保证应用服务器不具有状态,那么某个应用服务器故障时,负载均衡器将该应用服务器原先的用户请求转发到另一个应用服务器上不会对用户有任何影响。
|
||||
应用服务器的冗余比较容易实现,只要保证应用服务器不具有状态,那么某个应用服务器故障时,负载均衡器将该应用服务器原先的用户请求转发到另一个应用服务器上,不会对用户有任何影响。
|
||||
|
||||
存储服务器的冗余需要使用主从复制来实现,当主服务器故障时,需要提升从服务器为主服务器,这个过程称为切换。
|
||||
|
||||
@@ -97,7 +97,7 @@
|
||||
|
||||
## 服务降级
|
||||
|
||||
服务器降级是系统为了应对大量的请求,主动关闭部分功能,从而保证核心功能可用。
|
||||
服务降级是系统为了应对大量的请求,主动关闭部分功能,从而保证核心功能可用。
|
||||
|
||||
# 五、安全性
|
||||
|
||||
|
||||
+9
-21
@@ -6,6 +6,7 @@
|
||||
* [五、缓存问题](#五缓存问题)
|
||||
* [六、数据分布](#六数据分布)
|
||||
* [七、一致性哈希](#七一致性哈希)
|
||||
* [参考资料](#参考资料)
|
||||
<!-- GFM-TOC -->
|
||||
|
||||
|
||||
@@ -29,10 +30,6 @@
|
||||
|
||||
- LRU(Least Recently Used):最近最久未使用策略,优先淘汰最久未使用的数据,也就是上次被访问时间距离现在最远的数据。该策略可以保证内存中的数据都是热点数据,也就是经常被访问的数据,从而保证缓存命中率。
|
||||
|
||||
参考资料:
|
||||
|
||||
- [缓存那些事](https://tech.meituan.com/cache_about.html)
|
||||
|
||||
# 二、LRU
|
||||
|
||||
以下是一个基于 双向链表 + HashMap 的 LRU 算法实现,对算法的解释如下:
|
||||
@@ -143,10 +140,6 @@ public class LRU<K, V> implements Iterable<K> {
|
||||
}
|
||||
```
|
||||
|
||||
源代码:
|
||||
|
||||
- [CyC2018/Algorithm](https://github.com/CyC2018/Algorithm/tree/master/Caching)
|
||||
|
||||
# 三、缓存位置
|
||||
|
||||
## 浏览器
|
||||
@@ -169,7 +162,7 @@ public class LRU<K, V> implements Iterable<K> {
|
||||
|
||||
使用 Redis、Memcache 等分布式缓存将数据缓存在分布式缓存系统中。
|
||||
|
||||
相对于本地缓存来说,分布式缓存单独部署,可以根据需求分配硬件资源。不仅如此,服务器集群都可以访问分布式缓存。而本地缓存需要在服务器集群之间进行同步,实现和性能开销上都非常大。
|
||||
相对于本地缓存来说,分布式缓存单独部署,可以根据需求分配硬件资源。不仅如此,服务器集群都可以访问分布式缓存,而本地缓存需要在服务器集群之间进行同步,实现和性能开销上都非常大。
|
||||
|
||||
## 数据库缓存
|
||||
|
||||
@@ -177,7 +170,7 @@ MySQL 等数据库管理系统具有自己的查询缓存机制来提高 SQL 查
|
||||
|
||||
# 四、CDN
|
||||
|
||||
内容分发网络(Content distribution network,CDN)是一种通过互连的网络系统,利用更靠近用户的服务器更快更可靠地将 HTML、CSS、JavaScript、音乐、图片、视频等静态资源分发给用户。
|
||||
内容分发网络(Content distribution network,CDN)是一种互连的网络系统,它利用更靠近用户的服务器从而更快更可靠地将 HTML、CSS、JavaScript、音乐、图片、视频等静态资源分发给用户。
|
||||
|
||||
CDN 主要有以下优点:
|
||||
|
||||
@@ -187,11 +180,6 @@ CDN 主要有以下优点:
|
||||
|
||||
<div align="center"> <img src="../pics//15313ed8-a520-4799-a300-2b6b36be314f.jpg"/> </div><br>
|
||||
|
||||
参考资料:
|
||||
|
||||
- [内容分发网络](https://zh.wikipedia.org/wiki/%E5%85%A7%E5%AE%B9%E5%82%B3%E9%81%9E%E7%B6%B2%E8%B7%AF)
|
||||
- [How Aspiration CDN helps to improve your website loading speed?](https://www.aspirationhosting.com/aspiration-cdn/)
|
||||
|
||||
# 五、缓存问题
|
||||
|
||||
## 缓存穿透
|
||||
@@ -213,7 +201,7 @@ CDN 主要有以下优点:
|
||||
|
||||
- 为了防止缓存在同一时间大面积过期导致的缓存雪崩,可以通过观察用户行为,合理设置缓存过期时间来实现;
|
||||
- 为了防止缓存服务器宕机出现的缓存雪崩,可以使用分布式缓存,分布式缓存中每一个节点只缓存部分的数据,当某个节点宕机时可以保证其它节点的缓存仍然可用。
|
||||
- 也可以在进行缓存预热,避免在系统刚启动不久由于还未将大量数据进行缓存而导致缓存雪崩。
|
||||
- 也可以进行缓存预热,避免在系统刚启动不久由于还未将大量数据进行缓存而导致缓存雪崩。
|
||||
|
||||
## 缓存一致性
|
||||
|
||||
@@ -243,10 +231,6 @@ CDN 主要有以下优点:
|
||||
- 能保持数据原有的顺序;
|
||||
- 并且能够准确控制每台服务器存储的数据量,从而使得存储空间的利用率最大。
|
||||
|
||||
参考资料:
|
||||
|
||||
- 大规模分布式存储系统
|
||||
|
||||
# 七、一致性哈希
|
||||
|
||||
Distributed Hash Table(DHT) 是一种哈希分布方式,其目的是为了克服传统哈希分布在服务器节点数量变化时大量数据失效的问题。
|
||||
@@ -267,6 +251,10 @@ Distributed Hash Table(DHT) 是一种哈希分布方式,其目的是为了
|
||||
|
||||
数据不均匀主要是因为节点在哈希环上分布的不均匀,这种情况在节点数量很少的情况下尤其明显。解决方式是通过增加虚拟节点,然后将虚拟节点映射到真实节点上。虚拟节点的数量比真实节点来得大,那么虚拟节点在哈希环上分布的均匀性就会比原来的真实节点好,从而使得数据分布也更加均匀。
|
||||
|
||||
参考资料:
|
||||
# 参考资料
|
||||
|
||||
- 大规模分布式存储系统
|
||||
- [缓存那些事](https://tech.meituan.com/cache_about.html)
|
||||
- [一致性哈希算法](https://my.oschina.net/jayhu/blog/732849)
|
||||
- [内容分发网络](https://zh.wikipedia.org/wiki/%E5%85%A7%E5%AE%B9%E5%82%B3%E9%81%9E%E7%B6%B2%E8%B7%AF)
|
||||
- [How Aspiration CDN helps to improve your website loading speed?](https://www.aspirationhosting.com/aspiration-cdn/)
|
||||
|
||||
+40
-36
@@ -60,9 +60,9 @@
|
||||
|
||||
(一)懒汉式-线程不安全
|
||||
|
||||
以下实现中,私有静态变量 uniqueInstance 被延迟化实例化,这样做的好处是,如果没有用到该类,那么就不会实例化 uniqueInstance,从而节约资源。
|
||||
以下实现中,私有静态变量 uniqueInstance 被延迟实例化,这样做的好处是,如果没有用到该类,那么就不会实例化 uniqueInstance,从而节约资源。
|
||||
|
||||
这个实现在多线程环境下是不安全的,如果多个线程能够同时进入 `if (uniqueInstance == null)` ,并且此时 uniqueInstance 为 null,那么多个线程会执行 `uniqueInstance = new Singleton();` 语句,这将导致多次实例化 uniqueInstance。
|
||||
这个实现在多线程环境下是不安全的,如果多个线程能够同时进入 `if (uniqueInstance == null)` ,并且此时 uniqueInstance 为 null,那么会有多个线程执行 `uniqueInstance = new Singleton();` 语句,这将导致多次实例化 uniqueInstance。
|
||||
|
||||
```java
|
||||
public class Singleton {
|
||||
@@ -81,11 +81,21 @@ public class Singleton {
|
||||
}
|
||||
```
|
||||
|
||||
(二)懒汉式-线程安全
|
||||
(二)饿汉式-线程安全
|
||||
|
||||
只需要对 getUniqueInstance() 方法加锁,那么在一个时间点只能有一个线程能够进入该方法,从而避免了对 uniqueInstance 进行多次实例化的问题。
|
||||
线程不安全问题主要是由于 uniqueInstance 被多次实例化,采取直接实例化 uniqueInstance 的方式就不会产生线程不安全问题。
|
||||
|
||||
但是这样有一个问题,就是当一个线程进入该方法之后,其它线程试图进入该方法都必须等待,因此性能上有一定的损耗。
|
||||
但是直接实例化的方式也丢失了延迟实例化带来的节约资源的好处。
|
||||
|
||||
```java
|
||||
private static Singleton uniqueInstance = new Singleton();
|
||||
```
|
||||
|
||||
(三)懒汉式-线程安全
|
||||
|
||||
只需要对 getUniqueInstance() 方法加锁,那么在一个时间点只能有一个线程能够进入该方法,从而避免了多次实例化 uniqueInstance 的问题。
|
||||
|
||||
但是当一个线程进入该方法之后,其它试图进入该方法的线程都必须等待,因此性能上有一定的损耗。
|
||||
|
||||
```java
|
||||
public static synchronized Singleton getUniqueInstance() {
|
||||
@@ -96,16 +106,6 @@ public static synchronized Singleton getUniqueInstance() {
|
||||
}
|
||||
```
|
||||
|
||||
(三)饿汉式-线程安全
|
||||
|
||||
线程不安全问题主要是由于 uniqueInstance 被实例化了多次,如果 uniqueInstance 采用直接实例化的话,就不会被实例化多次,也就不会产生线程不安全问题。
|
||||
|
||||
但是直接实例化的方式也丢失了延迟实例化带来的节约资源的好处。
|
||||
|
||||
```java
|
||||
private static Singleton uniqueInstance = new Singleton();
|
||||
```
|
||||
|
||||
(四)双重校验锁-线程安全
|
||||
|
||||
uniqueInstance 只需要被实例化一次,之后就可以直接使用了。加锁操作只需要对实例化那部分的代码进行,只有当 uniqueInstance 没有被实例化时,才需要进行加锁。
|
||||
@@ -175,7 +175,7 @@ public class Singleton {
|
||||
}
|
||||
```
|
||||
|
||||
(五)枚举实现
|
||||
(六)枚举实现
|
||||
|
||||
这是单例模式的最佳实践,它实现简单,并且在面对复杂的序列化或者反射攻击的时候,能够防止实例化多次。
|
||||
|
||||
@@ -231,27 +231,9 @@ public class Singleton implements Serializable {
|
||||
|
||||
简单工厂不是设计模式,更像是一种编程习惯。它把实例化的操作单独放到一个类中,这个类就成为简单工厂类,让简单工厂类来决定应该用哪个具体子类来实例化。
|
||||
|
||||
<div align="center"> <img src="../pics//c79da808-0f28-4a36-bc04-33ccc5b83c13.png"/> </div><br>
|
||||
|
||||
这样做能把客户类和具体子类的实现解耦,客户类不再需要知道有哪些子类以及应当实例化哪个子类。因为客户类往往有多个,如果不使用简单工厂,所有的客户类都要知道所有子类的细节。而且一旦子类发生改变,例如增加子类,那么所有的客户类都要进行修改。
|
||||
|
||||
如果存在下面这种代码,就需要使用简单工厂将对象实例化的部分放到简单工厂中。
|
||||
|
||||
```java
|
||||
public class Client {
|
||||
public static void main(String[] args) {
|
||||
int type = 1;
|
||||
Product product;
|
||||
if (type == 1) {
|
||||
product = new ConcreteProduct1();
|
||||
} else if (type == 2) {
|
||||
product = new ConcreteProduct2();
|
||||
} else {
|
||||
product = new ConcreteProduct();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
<div align="center"> <img src="../pics//c79da808-0f28-4a36-bc04-33ccc5b83c13.png"/> </div><br>
|
||||
|
||||
### 实现
|
||||
|
||||
@@ -275,6 +257,27 @@ public class ConcreteProduct2 implements Product {
|
||||
}
|
||||
```
|
||||
|
||||
以下的 Client 类中包含了实例化的代码,这是一种错误的实现,如果在客户类中存在实例化代码,就需要将代码放到简单工厂中。
|
||||
|
||||
```java
|
||||
public class Client {
|
||||
public static void main(String[] args) {
|
||||
int type = 1;
|
||||
Product product;
|
||||
if (type == 1) {
|
||||
product = new ConcreteProduct1();
|
||||
} else if (type == 2) {
|
||||
product = new ConcreteProduct2();
|
||||
} else {
|
||||
product = new ConcreteProduct();
|
||||
}
|
||||
// do something with the product
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
以下的 SimpleFactory 是简单工厂实现,它被所有需要进行实例化的客户类调用。
|
||||
|
||||
```java
|
||||
public class SimpleFactory {
|
||||
public Product createProduct(int type) {
|
||||
@@ -293,6 +296,7 @@ public class Client {
|
||||
public static void main(String[] args) {
|
||||
SimpleFactory simpleFactory = new SimpleFactory();
|
||||
Product product = simpleFactory.createProduct(1);
|
||||
// do something with the product
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -2906,7 +2910,7 @@ Java 利用缓存来加速大量小对象的访问时间。
|
||||
- 远程代理(Remote Proxy):控制对远程对象(不同地址空间)的访问,它负责将请求及其参数进行编码,并向不同地址空间中的对象发送已经编码的请求。
|
||||
- 虚拟代理(Virtual Proxy):根据需要创建开销很大的对象,它可以缓存实体的附加信息,以便延迟对它的访问,例如在网站加载一个很大图片时,不能马上完成,可以用虚拟代理缓存图片的大小信息,然后生成一张临时图片代替原始图片。
|
||||
- 保护代理(Protection Proxy):按权限控制对象的访问,它负责检查调用者是否具有实现一个请求所必须的访问权限。
|
||||
- 智能代理(Smart Reference):取代了简单的指针,它在访问对象时执行一些附加操作:记录对象的引用次数,比如智能智能;当第一次引用一个持久化对象时,将它装入内存;在访问一个实际对象前,检查是否已经锁定了它,以确保其它对象不能改变它。
|
||||
- 智能代理(Smart Reference):取代了简单的指针,它在访问对象时执行一些附加操作:记录对象的引用次数;当第一次引用一个持久化对象时,将它装入内存;在访问一个实际对象前,检查是否已经锁定了它,以确保其它对象不能改变它。
|
||||
|
||||
<div align="center"> <img src="../pics//a6c20f60-5eba-427d-9413-352ada4b40fe.png"/> </div><br>
|
||||
|
||||
|
||||
+10
-5
@@ -37,6 +37,7 @@
|
||||
|
||||
```java
|
||||
public class Person {
|
||||
|
||||
private String name;
|
||||
private int gender;
|
||||
private int age;
|
||||
@@ -63,17 +64,20 @@ public class Person {
|
||||
|
||||
继承实现了 **IS-A** 关系,例如 Cat 和 Animal 就是一种 IS-A 关系,因此 Cat 可以继承自 Animal,从而获得 Animal 非 private 的属性和方法。
|
||||
|
||||
继承应该遵循里氏替换原则,子类对象必须能够替换掉所有父类对象。
|
||||
|
||||
Cat 可以当做 Animal 来使用,也就是说可以使用 Animal 引用 Cat 对象。父类引用指向子类对象称为 **向上转型** 。
|
||||
|
||||
```java
|
||||
Animal animal = new Cat();
|
||||
```
|
||||
|
||||
继承应该遵循里氏替换原则,子类对象必须能够替换掉所有父类对象。
|
||||
|
||||
## 多态
|
||||
|
||||
多态分为编译时多态和运行时多态。编译时多态主要指方法的重载,运行时多态指程序中定义的对象引用所指向的具体类型在运行期间才确定。
|
||||
多态分为编译时多态和运行时多态:
|
||||
|
||||
- 编译时多态主要指方法的重载
|
||||
- 运行时多态指程序中定义的对象引用所指向的具体类型在运行期间才确定
|
||||
|
||||
运行时多态有三个条件:
|
||||
|
||||
@@ -116,7 +120,7 @@ public class Music {
|
||||
|
||||
# 二、类图
|
||||
|
||||
以下类图使用 [PlantUML](https://www.planttext.com/) 绘制,更多语法及使用请参考:http://plantuml.com/
|
||||
以下类图使用 [PlantUML](https://www.planttext.com/) 绘制,更多语法及使用请参考:http://plantuml.com/ 。
|
||||
|
||||
## 泛化关系 (Generalization)
|
||||
|
||||
@@ -327,7 +331,7 @@ Vihicle .. N
|
||||
|
||||
### 2. 合成复用原则
|
||||
|
||||
尽量使用对象组合,而不是继承来达到复用的目的。
|
||||
尽量使用对象组合,而不是通过继承来达到复用的目的。
|
||||
|
||||
### 3. 共同封闭原则
|
||||
|
||||
@@ -349,3 +353,4 @@ Vihicle .. N
|
||||
- [看懂 UML 类图和时序图](http://design-patterns.readthedocs.io/zh_CN/latest/read_uml.html#generalization)
|
||||
- [UML 系列——时序图(顺序图)sequence diagram](http://www.cnblogs.com/wolf-sun/p/UML-Sequence-diagram.html)
|
||||
- [面向对象编程三大特性 ------ 封装、继承、多态](http://blog.csdn.net/jianyuerensheng/article/details/51602015)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user