diff --git a/README.md b/README.md index 7ae8492f..21ddb6a7 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,13 @@ -| Ⅰ | Ⅱ | Ⅲ | Ⅳ | Ⅴ | Ⅵ | Ⅶ | Ⅷ | Ⅸ | Ⅹ | Ⅺ | -| :--------: | :---------: | :---------: | :---------: | :---------: | :---------:| :---------: | :-------: | :-------:| :------:| :-------:| -|网络[:cloud:](#网络-cloud) |操作系统[:computer:](#操作系统-computer)| 算法[:pencil2:](#数据结构与算法-pencil2)| 面向对象[:couple:](#面向对象-couple) |数据库[:floppy_disk:](#数据库-floppy_disk)| Java [:coffee:](#java-coffee)| 分布式[:sweat_drops:](#分布式-sweat_drops)| 工具[:hammer:](#工具-hammer)| 编码实践[:speak_no_evil:](#编码实践-speak_no_evil)| 资料下载[:arrow_down:](#资料下载-arrow_down)| 后记[:memo:](#后记-memo) | +| Ⅰ | Ⅱ | Ⅲ | Ⅳ | Ⅴ | Ⅵ | Ⅶ | Ⅷ | Ⅸ | Ⅹ | +| :--------: | :---------: | :---------: | :---------: | :---------: | :---------:| :---------: | :-------: | :-------:| :------:| +|网络[:cloud:](#网络-cloud) |操作系统[:computer:](#操作系统-computer)| 算法[:pencil2:](#数据结构与算法-pencil2)| 面向对象[:couple:](#面向对象-couple) |数据库[:floppy_disk:](#数据库-floppy_disk)| Java [:coffee:](#java-coffee)| 分布式[:sweat_drops:](#分布式-sweat_drops)| 工具[:hammer:](#工具-hammer)| 编码实践[:speak_no_evil:](#编码实践-speak_no_evil)| 后记[:memo:](#后记-memo) | +
+ +:loudspeaker: 本仓库的内容不涉及商业行为,不向读者收取任何费用。 + +:loudspeaker: This repository is not involving commercial activities, and does not charge readers any fee. +

## 网络 :cloud: @@ -129,23 +135,40 @@ File, InputStream OutputStream, Reader Writer, Serializable, Socket, NIO Google 开源项目的代码风格规范。 -## 资料下载 :arrow_down: - -> [Download](https://github.com/CyC2018/Interview-Notebook/blob/master/other/download.md) - ## 后记 :memo: -> [Postface](https://github.com/CyC2018/Interview-Notebook/blob/master/other/postface.md) +**关于仓库** - +因为大部分内容是笔者一个字一个字打上去的,所有难免会有一些笔误。如果发现,可以直接在相应的文档上编辑修改。 -## License +笔者能力有限,很多内容还不够完善。如果您希望和笔者一起完善这个仓库,可以在发表一个 Issue,表明您想要添加的内容,笔者会及时查看。 -知识共享许可协议 +因为不打算将这个仓库做成一个大而全的面试宝典,只希望添加一些比较通用的基础知识,或者是与 Java 和分布式相关的内容,但是不添加 Java Web 相关的内容。 + +您也可以在 Issues 中发表关于改进本仓库的建议。 + +**关于上传** + +笔者在本地使用为知笔记软件进行书写,为了方便将本地笔记内容上传到 Github 上,实现了一整套自动化上传方案,包括文本文件的导出、提取图片、Markdown 文档转换、Git 同步。 + +进行 Markdown 文档转换是因为 Github 使用的 GFM 不支持 MathJax 公式和 TOC 标记,所以需要替换 MathJax 公式为 CodeCogs 的云服务和重新生成 TOC 目录。这里提供了笔者实现的 GFM 文档转换工具的下载:[GFM-Converter](https://github.com/CyC2018/GFM-Converter)。 + +**关于排版** + +笔记内容按照 [中文文案排版指北](http://mazhuang.org/wiki/chinese-copywriting-guidelines/#%E4%B8%8D%E8%A6%81%E4%BD%BF%E7%94%A8%E4%B8%8D%E5%9C%B0%E9%81%93%E7%9A%84%E7%BC%A9%E5%86%99) 进行排版,以保证内容的可读性。这里提供了笔者实现的中英混排文档在线排版工具:[Text-Typesetting](https://github.com/CyC2018/Markdown-Typesetting),目前实现了加空格的功能,之后打算实现对英文专有名词提示首字母大写的功能。 + +不使用 `![]()` 这种方式来引用图片是为了能够控制图片以合适的大小显示。而且 GFM 不支持 `
![]()
` 让图片居中显示,只能使用 `
` ,所以只能使用 img 标签来引用图片。 + +**关于转载** + +本仓库内容使用到的资料都会在最后面的参考资料中给出引用链接,希望您在使用本仓库的内容时也能给出相应的引用链接。 + +**鸣谢** + +[TeeKee](https://github.com/linw7) -本作品采用 知识共享署名-非商业性使用-相同方式共享 3.0 中国大陆许可协议 进行许可。 diff --git a/notes/Git.md b/notes/Git.md index 2041b8f5..1695faad 100644 --- a/notes/Git.md +++ b/notes/Git.md @@ -79,7 +79,7 @@ Git 把每次提交都连成一条时间线。分支使用指针来实现,例 # 冲突 -当两个分支都对同一个文件进行了修改,在分支合并时就会产生冲突。 +当两个分支都对同一个文件的同一行进行了修改,在分支合并时就会产生冲突。

diff --git a/notes/HTTP.md b/notes/HTTP.md index a20cc03c..4dd80655 100644 --- a/notes/HTTP.md +++ b/notes/HTTP.md @@ -5,8 +5,8 @@ * [请求和响应报文](#请求和响应报文) * [二、HTTP 方法](#二http-方法) * [GET](#get) - * [POST](#post) * [HEAD](#head) + * [POST](#post) * [PUT](#put) * [PATCH](#patch) * [DELETE](#delete) @@ -27,8 +27,9 @@ * [Cookie](#cookie) * [缓存](#缓存) * [持久连接](#持久连接) + * [管线化处理](#管线化处理) * [编码](#编码) - * [分块传输](#分块传输) + * [分块传输编码](#分块传输编码) * [多部分对象集合](#多部分对象集合) * [范围请求](#范围请求) * [内容协商](#内容协商) @@ -41,10 +42,16 @@ * [七、Web 攻击技术](#七web-攻击技术) * [攻击模式](#攻击模式) * [跨站脚本攻击](#跨站脚本攻击) - * [SQL 注入攻击](#sql-注入攻击) * [跨站点请求伪造](#跨站点请求伪造) + * [SQL 注入攻击](#sql-注入攻击) * [拒绝服务攻击](#拒绝服务攻击) -* [八、各版本比较](#八各版本比较) +* [八、GET 和 POST 的区别](#八get-和-post-的区别) + * [参数](#参数) + * [安全](#安全) + * [幂等性](#幂等性) + * [可缓存](#可缓存) + * [XMLHttpRequest](#xmlhttprequest) +* [九、各版本比较](#九各版本比较) * [HTTP/1.0 与 HTTP/1.1 的区别](#http10-与-http11-的区别) * [HTTP/1.1 与 HTTP/2.0 的区别](#http11-与-http20-的区别) * [参考资料](#参考资料) @@ -67,17 +74,17 @@ URI 包含 URL 和 URN,目前 WEB 只有 URL 比较流行,所以见到的基本都是 URL。 -

+

## 请求和响应报文 ### 1. 请求报文 -

+

### 2. 响应报文 -

+

# 二、HTTP 方法 @@ -89,28 +96,6 @@ URI 包含 URL 和 URN,目前 WEB 只有 URL 比较流行,所以见到的基 当前网络请求中,绝大部分使用的是 GET 方法。 -## POST - -> 传输实体主体 - -POST 主要目的不是获取资源,而是传输存储在内容实体中的数据。 - -GET 和 POST 的请求都能使用额外的参数,但是 GET 的参数是以查询字符串出现在 URL 中,而 POST 的参数存储在内容实体。 - -``` -GET /test/demo_form.asp?name1=value1&name2=value2 HTTP/1.1 -``` - -``` -POST /test/demo_form.asp HTTP/1.1 -Host: w3schools.com -name1=value1&name2=value2 -``` - -GET 的传参方式相比于 POST 安全性较差,因为 GET 传的参数在 URL 中是可见的,可能会泄露私密信息。并且 GET 只支持 ASCII 字符,如果参数为中文则可能会出现乱码,而 POST 支持标准字符集。 - -GET 和 POST 的另一个区别是,使用 GET 方法,浏览器会把 HTTP Header 和 Data 一并发送出去,服务器响应 200(OK)并返回数据。而使用 POST 方法,浏览器先发送 Header,服务器响应 100(Continue)之后,浏览器再发送 Data,最后服务器响应 200(OK)并返回数据。 - ## HEAD > 获取报文首部 @@ -119,6 +104,14 @@ GET 和 POST 的另一个区别是,使用 GET 方法,浏览器会把 HTTP He 主要用于确认 URL 的有效性以及资源更新的日期时间等。 +## POST + +> 传输实体主体 + +POST 主要用来传输数据,而 GET 主要用来获取资源。 + +更多 POST 与 GET 的比较请见第八章。 + ## PUT > 上传文件 @@ -172,13 +165,13 @@ DELETE /file.html HTTP/1.1 > 要求用隧道协议连接代理 -要求在于代理服务器通信时建立隧道,使用 SSL(Secure Sokets Layer,安全套接字)和 TLS(Transport Layer Security,传输层安全)协议把通信内容加密后经网络隧道传输。 +要求在与代理服务器通信时建立隧道,使用 SSL(Secure Sockets Layer,安全套接层)和 TLS(Transport Layer Security,传输层安全)协议把通信内容加密后经网络隧道传输。 ```html CONNECT www.example.com:443 HTTP/1.1 ``` -

+

## TRACE @@ -190,8 +183,6 @@ CONNECT www.example.com:443 HTTP/1.1 通常不会使用 TRACE,并且它容易受到 XST 攻击(Cross-Site Tracing,跨站追踪),因此更不会去使用它。 -

- # 三、HTTP 状态码 服务器返回的 **响应报文** 中第一行为状态行,包含了状态码以及原因短语,用来告知客户端请求的结果。 @@ -204,6 +195,10 @@ CONNECT www.example.com:443 HTTP/1.1 | 4XX | Client Error(客户端错误状态码) | 服务器无法处理请求 | | 5XX | Server Error(服务器错误状态码) | 服务器处理请求出错 | +### 1XX 信息 + +- **100 Continue** :表明到目前为止都很正常,客户端可以继续发送请求或者忽略这个响应。 + ## 2XX 成功 - **200 OK** @@ -220,9 +215,9 @@ CONNECT www.example.com:443 HTTP/1.1 - **303 See Other** :和 302 有着相同的功能,但是 303 明确要求客户端应该采用 GET 方法获取资源。 -- 注:虽然 HTTP 协议规定 301、302 状态下重定向时不允许把 POST 方法改成 GET 方法,但是大多数浏览器都会 在 301、302 和 303 状态下的重定向把 POST 方法改成 GET 方法。 +- 注:虽然 HTTP 协议规定 301、302 状态下重定向时不允许把 POST 方法改成 GET 方法,但是大多数浏览器都会在 301、302 和 303 状态下的重定向把 POST 方法改成 GET 方法。 -- **304 Not Modified** :如果请求报文首部包含一些条件,例如:If-Match,If-ModifiedSince,If-None-Match,If-Range,If-Unmodified-Since,但是不满足条件,则服务器会返回 304 状态码。 +- **304 Not Modified** :如果请求报文首部包含一些条件,例如:If-Match,If-ModifiedSince,If-None-Match,If-Range,If-Unmodified-Since,如果不满足条件,则服务器会返回 304 状态码。 - **307 Temporary Redirect** :临时重定向,与 302 的含义类似,但是 307 要求浏览器不会把重定向请求的 POST 方法改成 GET 方法。 @@ -232,8 +227,6 @@ CONNECT www.example.com:443 HTTP/1.1 - **401 Unauthorized** :该状态码表示发送的请求需要有认证信息(BASIC 认证、DIGEST 认证)。如果之前已进行过一次请求,则表示用户认证失败。 -

- - **403 Forbidden** :请求被拒绝,服务器端没有必要给出拒绝的详细理由。 - **404 Not Found** @@ -323,7 +316,7 @@ CONNECT www.example.com:443 HTTP/1.1 HTTP 协议是无状态的,主要是为了让 HTTP 协议尽可能简单,使得它能够处理大量事务。HTTP/1.1 引入 Cookie 来保存状态信息。 -Cookie 是服务器发送给客户端的数据,该数据会被保存在浏览器中,并且在下一次发送请求时包含该数据。通过 Cookie 可以让服务器知道两个请求是否来自于同一个客户端,从而实现保持登录状态等功能。 +Cookie 是服务器发送给客户端的数据,该数据会被保存在浏览器中,并且客户端的下一次请求报文会包含该数据。通过 Cookie 可以让服务器知道两个请求是否来自于同一个客户端,从而实现保持登录状态等功能。 ### 1. 创建过程 @@ -346,7 +339,16 @@ Host: www.example.org Cookie: yummy_cookie=choco; tasty_cookie=strawberry ``` -### 2. Set-Cookie +### 2. 分类 + +- 会话期 Cookie:浏览器关闭之后它会被自动删除,也就是说它仅在会话期内有效。 +- 持久性 Cookie:指定一个特定的过期时间(Expires)或有效期(Max-Age)之后就成为了持久性的 Cookie。 + +```html +Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; +``` + +### 3. Set-Cookie | 属性 | 说明 | | :--: | -- | @@ -354,21 +356,23 @@ Cookie: yummy_cookie=choco; tasty_cookie=strawberry | expires=DATE | Cookie 的有效期(若不明确指定则默认为浏览器关闭前为止) | | path=PATH | 将服务器上的文件目录作为 Cookie 的适用对象(若不指定则默认为文档所在的文件目录) | | domain=域名 | 作为 Cookie 适用对象的域名(若不指定则默认为创建 Cookie 的服务器的域名) | -| Secure | 仅在 HTTPS 安全通信时才会发送 Cookie | +| Secure | 仅在 HTTPs 安全通信时才会发送 Cookie | | HttpOnly | 加以限制,使 Cookie 不能被 JavaScript 脚本访问 | -### 3. Session 和 Cookie 区别 +### 4. Session 和 Cookie 区别 -Session 是服务器用来跟踪用户的一种手段,每个 Session 都有一个唯一标识:Session ID。当服务器创建了一个 Session 时,给客户端发送的响应报文就包含了 Set-Cookie 字段,其中有一个名为 sid 的键值对,这个键值对就是 Session ID。客户端收到后就把 Cookie 保存在浏览器中,并且之后发送的请求报文都包含 Session ID。HTTP 就是通过 Session 和 Cookie 这两种方式一起合作来实现跟踪用户状态的,Session 用于服务器端,Cookie 用于客户端。 +Session 是服务器用来跟踪用户的一种手段,每个 Session 都有一个唯一标识:Session ID。当服务器创建了一个 Session 时,给客户端发送的响应报文包含了 Set-Cookie 字段,其中有一个名为 sid 的键值对,这个键值对就是 Session ID。客户端收到后就把 Cookie 保存在浏览器中,并且之后发送的请求报文都包含 Session ID。HTTP 就是通过 Session 和 Cookie 这两种方式一起合作来实现跟踪用户状态的,Session 用于服务器端,Cookie 用于客户端。 -### 4. 浏览器禁用 Cookie 的情况 +### 5. 浏览器禁用 Cookie 的情况 会使用 URL 重写技术,在 URL 后面加上 sid=xxx 。 -### 5. 使用 Cookie 实现用户名和密码的自动填写 +### 6. 使用 Cookie 实现用户名和密码的自动填写 网站脚本会自动从保存在浏览器中的 Cookie 读取用户名和密码,从而实现自动填写。 +但是如果 Set-Cookie 指定了 HttpOnly 属性,就无法通过 Javascript 脚本获取 Cookie 信息,这是出于安全性考虑。 + ## 缓存 ### 1. 优点 @@ -413,21 +417,21 @@ Expires 字段也可以用于告知缓存服务器该资源什么时候会过期 当浏览器访问一个包含多张图片的 HTML 页面时,除了请求访问 HTML 页面资源,还会请求图片资源,如果每进行一次 HTTP 通信就要断开一次 TCP 连接,连接建立和断开的开销会很大。持久连接只需要建立一次 TCP 连接就能进行多次 HTTP 通信。 -

+

持久连接需要使用 Connection 首部字段进行管理。HTTP/1.1 开始 HTTP 默认是持久化连接的,如果要断开 TCP 连接,需要由客户端或者服务器端提出断开,使用 Connection : close;而在 HTTP/1.1 之前默认是非持久化连接的,如果要维持持续连接,需要使用 Connection : Keep-Alive。 -**管线化方式** 可以同时发送多个请求和响应,而不需要发送一个请求然后等待响应之后再发下一个请求。 +## 管线化处理 -

+HTTP/1.1 支持管线化处理,可以同时发送多个请求和响应,而不需要发送一个请求然后等待响应之后再发下一个请求。 ## 编码 编码(Encoding)主要是为了对实体进行压缩。常用的编码有:gzip、compress、deflate、identity,其中 identity 表示不执行压缩的编码格式。 -## 分块传输 +## 分块传输编码 -分块传输(Chunked Transfer Coding)可以把数据分割成多块,让浏览器逐步显示页面。 +Chunked Transfer Coding,可以把数据分割成多块,让浏览器逐步显示页面。 ## 多部分对象集合 @@ -454,7 +458,7 @@ Content-Type: text/plain 如果网络出现中断,服务器只发送了一部分数据,范围请求使得客户端能够只请求未发送的那部分数据,从而避免服务器端重新发送所有数据。 -在请求报文首部中添加 Range 字段,然后指定请求的范围,例如 Range:bytes=5001-10000。请求成功的话服务器发送 206 Partial Content 状态。 +在请求报文首部中添加 Range 字段指定请求的范围,请求成功的话服务器发送 206 Partial Content 状态。 ```html GET /z4d4kWk.jpg HTTP/1.1 @@ -478,7 +482,9 @@ Content-Length: 1024 ## 虚拟主机 -使用虚拟主机技术,使得一台服务器拥有多个域名,并且在逻辑上可以看成多个服务器。 +HTTP/1.1 使用虚拟主机技术,使得一台服务器拥有多个域名,并且在逻辑上可以看成多个服务器。 + +使用 Host 首部字段进行处理。 ## 通信数据转发 @@ -486,23 +492,21 @@ Content-Length: 1024 代理服务器接受客户端的请求,并且转发给其它服务器。 -代理服务器一般是透明的,不会改变 URL。 - 使用代理的主要目的是:缓存、网络访问控制以及访问日志记录。 -

+代理服务器分为正向代理和反向代理两种,用户察觉得到正向代理的存在,而反向代理一般位于内部网络中,用户察觉不到。 + +

+ +

### 2. 网关 与代理服务器不同的是,网关服务器会将 HTTP 转化为其它协议进行通信,从而请求其它非 HTTP 服务器的服务。 -

- ### 3. 隧道 -使用 SSL 等加密手段,为客户端和服务器之间建立一条安全的通信线路。 - -

+使用 SSL 等加密手段,为客户端和服务器之间建立一条安全的通信线路。隧道本身不去解析 HTTP 请求。 # 六、HTTPs @@ -512,17 +516,37 @@ HTTP 有以下安全性问题: 2. 不验证通信方的身份,通信方的身份有可能遭遇伪装; 3. 无法证明报文的完整性,报文有可能遭篡改。 -HTTPs 并不是新协议,而是 HTTP 先和 SSL(Secure Socket Layer)通信,再由 SSL 和 TCP 通信。通过使用 SSL,HTTPs 提供了加密、认证和完整性保护。 +HTTPs 并不是新协议,而是 HTTP 先和 SSL(Secure Sockets Layer)通信,再由 SSL 和 TCP 通信。也就是说使用了隧道进行通信。 + +通过使用 SSL,HTTPs 具有了加密、认证和完整性保护。 + +

## 加密 -有两种加密方式:对称密钥加密和公开密钥加密。对称密钥加密的加密和解密使用同一密钥,而公开密钥加密使用一对密钥用于加密和解密,分别为公开密钥和私有密钥。公开密钥所有人都可以获得,通信发送方获得接收方的公开密钥之后,就可以使用公开密钥进行加密,接收方收到通信内容后使用私有密钥解密。 +### 1. 对称密钥加密 -对称密钥加密的缺点:无法安全传输密钥;公开密钥加密的缺点:相对来说更耗时。 +Symmetric-Key Encryption,加密的加密和解密使用同一密钥。 -HTTPs 采用 **混合的加密机制** ,使用公开密钥加密用于传输对称密钥,之后使用对称密钥加密进行通信。(下图中,共享密钥即对称密钥) +- 优点:运算速度快; +- 缺点:密钥容易被获取。 -

+

+ +### 2. 公开密钥加密 + +Public-Key Encryption,使用一对密钥用于加密和解密,分别为公开密钥和私有密钥。公开密钥所有人都可以获得,通信发送方获得接收方的公开密钥之后,就可以使用公开密钥进行加密,接收方收到通信内容后使用私有密钥解密。 + +- 优点:更为安全; +- 缺点:运算速度慢; + +

+ +### 3. HTTPs 采用的加密方式 + +HTTPs 采用混合的加密机制,使用公开密钥加密用于传输对称密钥,之后使用对称密钥加密进行通信。(下图中的 Session Key 就是对称密钥) + +

## 认证 @@ -532,18 +556,16 @@ HTTPs 采用 **混合的加密机制** ,使用公开密钥加密用于传输 进行 HTTPs 通信时,服务器会把证书发送给客户端,客户端取得其中的公开密钥之后,先进行验证,如果验证通过,就可以开始通信。 -除了上诉提到的服务器端证书之外,还有客户端证书,客户端证书的目的就是让服务器对客户端进行验证。客户端证书需要用户自行安装,只有在业务需要非常高的安全性时才使用客户端证书,例如网上银行。 +

使用 OpenSSL 这套开源程序,每个人都可以构建一套属于自己的认证机构,从而自己给自己颁发服务器证书。浏览器在访问该服务器时,会显示“无法确认连接安全性”或“该网站的安全证书存在问题”等警告消息。 ## 完整性 -SSL 提供摘要功能来验证完整性。 +SSL 提供报文摘要功能来验证完整性。 # 七、Web 攻击技术 -Web 攻击的主要目标是使用 HTTP 协议的 Web 应用。 - ## 攻击模式 ### 1. 主动攻击 @@ -552,13 +574,27 @@ Web 攻击的主要目标是使用 HTTP 协议的 Web 应用。 ### 2. 被动攻击 -设下圈套,让用户发送有攻击代码的 HTTP 请求,那么用户发送了该 HTTP 请求之后就会泄露 Cookie 等个人信息,具有代表性的有跨站脚本攻击和跨站请求伪造。 +设下圈套,让用户发送有攻击代码的 HTTP 请求,用户会泄露 Cookie 等个人信息,具有代表性的有跨站脚本攻击和跨站请求伪造。 ## 跨站脚本攻击 ### 1. 概念 -(Cross-Site Scripting, XSS),可以将代码注入到用户浏览的网页上,这种代码包含 HTML 和 JavaScript。通过利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序。攻击成功后,攻击者可能得到更高的权限(如执行一些操作)、私密网页内容、会话和 Cookie 等各种内容。 +跨站脚本攻击(Cross-Site Scripting, XSS),可以将代码注入到用户浏览的网页上,这种代码包括 HTML 和 JavaScript。利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序。攻击成功后,攻击者可能得到更高的权限(如执行一些操作)、私密网页内容、会话和 Cookie 等各种内容。 + +例如有一个论坛网站,攻击者可以在上面发表以下内容: + +```html + +``` + +之后该内容可能会被渲染成以下形式: + +```html +

+``` + +另一个用户浏览了含有这个内容的页面将会跳往 domain.com 并携带了当前作用域的 Cookie。如果这个论坛网站通过 Cookie 管理用户登录状态,那么攻击者就可以通过这个 Cookie 登录被攻击者的账号了。 ### 2. 危害 @@ -568,7 +604,7 @@ Web 攻击的主要目标是使用 HTTP 协议的 Web 应用。 ### 3. 防范手段 -**(一)过滤特殊字符** +(一)过滤特殊字符 许多语言都提供了对 HTML 的过滤: @@ -577,7 +613,7 @@ Web 攻击的主要目标是使用 HTTP 协议的 Web 应用。 - Java 的 xssprotect (Open Source Library)。 - Node.js 的 node-validator。 -**(二)指定 HTTP 的 Content-Type** +(二)指定 HTTP 的 Content-Type 通过这种方式,可以避免内容被当成 HTML 解析,比如 PHP 语言可以使用以下代码: @@ -587,6 +623,44 @@ Web 攻击的主要目标是使用 HTTP 协议的 Web 应用。 ?> ``` +## 跨站点请求伪造 + +### 1. 概念 + +跨站点请求伪造(Cross-site request forgery,CSRF),是攻击者通过一些技术手段欺骗用户的浏览器去访问一个自己曾经认证过的网站并执行一些操作(如发邮件,发消息,甚至财产操作如转账和购买商品)。由于浏览器曾经认证过,所以被访问的网站会认为是真正的用户操作而去执行。这利用了 Web 中用户身份验证的一个漏洞:简单的身份验证只能保证请求发自某个用户的浏览器,却不能保证请求本身是用户自愿发出的。 + +XSS 利用的是用户对指定网站的信任,CSRF 利用的是网站对用户网页浏览器的信任。 + +假如一家银行用以执行转账操作的 URL 地址如下: + +``` +http://www.examplebank.com/withdraw?account=AccoutName&amount=1000&for=PayeeName。 +``` + +那么,一个恶意攻击者可以在另一个网站上放置如下代码: + +``` +。 +``` + +如果有账户名为 Alice 的用户访问了恶意站点,而她之前刚访问过银行不久,登录信息尚未过期,那么她就会损失 1000 资金。 + +这种恶意的网址可以有很多种形式,藏身于网页中的许多地方。此外,攻击者也不需要控制放置恶意网址的网站。例如他可以将这种地址藏在论坛,博客等任何用户生成内容的网站中。这意味着如果服务器端没有合适的防御措施的话,用户即使访问熟悉的可信网站也有受攻击的危险。 + +透过例子能够看出,攻击者并不能通过 CSRF 攻击来直接获取用户的账户控制权,也不能直接窃取用户的任何信息。他们能做到的,是欺骗用户浏览器,让其以用户的名义执行操作。 + +### 2. 防范手段 + +(一)检查 Referer 字段 + +HTTP 头中有一个 Referer 字段,这个字段用以标明请求来源于哪个地址。在处理敏感数据请求时,通常来说,Referer 字段应和请求的地址位于同一域名下。 + +这种办法简单易行,工作量低,仅需要在关键访问处增加一步校验。但这种办法也有其局限性,因其完全依赖浏览器发送正确的 Referer 字段。虽然 HTTP 协议对此字段的内容有明确的规定,但并无法保证来访的浏览器的具体实现,亦无法保证浏览器没有安全漏洞影响到此字段。并且也存在攻击者攻击某些浏览器,篡改其 Referer 字段的可能。 + +(二)添加校验 Token + +由于 CSRF 的本质在于攻击者欺骗用户去访问自己设置的地址,所以如果要求在访问敏感数据请求时,要求用户浏览器提供不保存在 cookie 中,并且攻击者无法伪造的数据作为校验,那么攻击者就无法再执行 CSRF 攻击。这种数据通常是表单中的一个数据项。服务器将其生成并附加在表单中,其内容是一个伪乱数。当客户端通过表单提交请求时,这个伪乱数也一并提交上去以供校验。正常的访问时,客户端浏览器能够正确得到并传回这个伪乱数,而通过 CSRF 传来的欺骗性攻击中,攻击者无从事先得知这个伪乱数的值,服务器端就会因为校验 token 的值为空或者错误,拒绝这个可疑请求。 + ## SQL 注入攻击 ### 1. 概念 @@ -637,36 +711,6 @@ strSQL = "SELECT * FROM users;" - 其他,使用其他更安全的方式连接 SQL 数据库。例如已修正过 SQL 注入问题的数据库连接组件,例如 ASP.NET 的 SqlDataSource 对象或是 LINQ to SQL。 - 使用 SQL 防注入系统。 -## 跨站点请求伪造 - -### 1. 概念 - -(Cross-site request forgery,XSRF),是攻击者通过一些技术手段欺骗用户的浏览器去访问一个自己曾经认证过的网站并执行一些操作(如发邮件,发消息,甚至财产操作如转账和购买商品)。由于浏览器曾经认证过,所以被访问的网站会认为是真正的用户操作而去执行。这利用了 Web 中用户身份验证的一个漏洞:简单的身份验证只能保证请求发自某个用户的浏览器,却不能保证请求本身是用户自愿发出的。 - -XSS 利用的是用户对指定网站的信任,CSRF 利用的是网站对用户网页浏览器的信任。 - -假如一家银行用以执行转账操作的 URL 地址如下:`http://www.examplebank.com/withdraw?account=AccoutName&amount=1000&for=PayeeName`。 - -
那么,一个恶意攻击者可以在另一个网站上放置如下代码:``。

- -如果有账户名为 Alice 的用户访问了恶意站点,而她之前刚访问过银行不久,登录信息尚未过期,那么她就会损失 1000 资金。 - -这种恶意的网址可以有很多种形式,藏身于网页中的许多地方。此外,攻击者也不需要控制放置恶意网址的网站。例如他可以将这种地址藏在论坛,博客等任何用户生成内容的网站中。这意味着如果服务器端没有合适的防御措施的话,用户即使访问熟悉的可信网站也有受攻击的危险。 - -透过例子能够看出,攻击者并不能通过 CSRF 攻击来直接获取用户的账户控制权,也不能直接窃取用户的任何信息。他们能做到的,是欺骗用户浏览器,让其以用户的名义执行操作。 - -### 2. 防范手段 - -**(一)检查 Referer 字段** - -HTTP 头中有一个 Referer 字段,这个字段用以标明请求来源于哪个地址。在处理敏感数据请求时,通常来说,Referer 字段应和请求的地址位于同一域名下。 - -这种办法简单易行,工作量低,仅需要在关键访问处增加一步校验。但这种办法也有其局限性,因其完全依赖浏览器发送正确的 Referer 字段。虽然 HTTP 协议对此字段的内容有明确的规定,但并无法保证来访的浏览器的具体实现,亦无法保证浏览器没有安全漏洞影响到此字段。并且也存在攻击者攻击某些浏览器,篡改其 Referer 字段的可能。 - -**(二)添加校验 Token** - -由于 CSRF 的本质在于攻击者欺骗用户去访问自己设置的地址,所以如果要求在访问敏感数据请求时,要求用户浏览器提供不保存在 cookie 中,并且攻击者无法伪造的数据作为校验,那么攻击者就无法再执行 CSRF 攻击。这种数据通常是表单中的一个数据项。服务器将其生成并附加在表单中,其内容是一个伪乱数。当客户端通过表单提交请求时,这个伪乱数也一并提交上去以供校验。正常的访问时,客户端浏览器能够正确得到并传回这个伪乱数,而通过 CSRF 传来的欺骗性攻击中,攻击者无从事先得知这个伪乱数的值,服务器端就会因为校验 token 的值为空或者错误,拒绝这个可疑请求。 - ## 拒绝服务攻击 ### 1. 概念 @@ -677,17 +721,91 @@ HTTP 头中有一个 Referer 字段,这个字段用以标明请求来源于哪 > [维基百科:拒绝服务攻击](https://zh.wikipedia.org/wiki/%E9%98%BB%E6%96%B7%E6%9C%8D%E5%8B%99%E6%94%BB%E6%93%8A) -# 八、各版本比较 +# 八、GET 和 POST 的区别 + +## 参数 + +GET 和 POST 的请求都能使用额外的参数,但是 GET 的参数是以查询字符串出现在 URL 中,而 POST 的参数存储在内容实体。 + +GET 的传参方式相比于 POST 安全性较差,因为 GET 传的参数在 URL 中是可见的,可能会泄露私密信息。并且 GET 只支持 ASCII 字符,如果参数为中文则可能会出现乱码,而 POST 支持标准字符集。 + +``` +GET /test/demo_form.asp?name1=value1&name2=value2 HTTP/1.1 +``` + +``` +POST /test/demo_form.asp HTTP/1.1 +Host: w3schools.com +name1=value1&name2=value2 +``` + +## 安全 + +安全的 HTTP 方法不会改变服务器状态,也就是说它只是可读的。 + +GET 方法是安全的,而 POST 却不是。 + +安全的方法除了 GET 之外还有:HEAD、OPTIONS。 + +不安全的方法除了 POST 之外还有 PUT、DELETE。 + +## 幂等性 + +幂等的 HTTP 方法,同样的请求被执行一次与连续执行多次的效果是一样的,服务器的状态也是一样的。换句话说就是,幂等方法不应该具有副作用(统计用途除外)。在正确实现的条件下,GET,HEAD,PUT和 DELETE 等方法都是幂等的,而 POST 方法不是。所有的安全方法也都是幂等的。 + +GET /pageX HTTP/1.1是幂等的。连续调用多次,客户端接收到的结果都是一样的: + +``` +GET /pageX HTTP/1.1 +GET /pageX HTTP/1.1 +GET /pageX HTTP/1.1 +GET /pageX HTTP/1.1 +``` + +POST /add_row HTTP/1.1不是幂等的。如果调用多次,就会增加多行记录: + +``` +POST /add_row HTTP/1.1 +POST /add_row HTTP/1.1 -> Adds a 2nd row +POST /add_row HTTP/1.1 -> Adds a 3rd row +``` + +DELETE /idX/delete HTTP/1.1是幂等的,即便是不同请求之间接收到的状态码不一样: + +``` +DELETE /idX/delete HTTP/1.1 -> Returns 200 if idX exists +DELETE /idX/delete HTTP/1.1 -> Returns 404 as it just got deleted +DELETE /idX/delete HTTP/1.1 -> Returns 404 +``` + +## 可缓存 + +如果要对响应进行缓存,需要满足以下条件: + +1. 请求报文的 HTTP 方法本身是可缓存的,包括 GET 和 HEAD,PUT 和 DELETE 不可缓存,POST 在多数情况下不可缓存的。 +2. 响应报文的 状态码是可缓存的,包括: 200, 203, 204, 206, 300, 301, 404, 405, 410, 414, and 501。 +3. 响应报文的 Cache-Control 首部字段没有指定不进行缓存。 + +## XMLHttpRequest + +为了阐述 POST 和 GET 的另一个区别,需要先了解 XMLHttpRequest: + +> XMLHttpRequest 是一个 API,它为客户端提供了在客户端和服务器之间传输数据的功能。它提供了一个通过 URL 来获取数据的简单方式,并且不会使整个页面刷新。这使得网页只更新一部分页面而不会打扰到用户。XMLHttpRequest 在 AJAX 中被大量使用。 + +在使用 XMLHttpRequest 的 POST 方法时,浏览器会先发送 Header 再发送 Data。但并不是所有浏览器会这么做,例如火狐就不会。 + +# 九、各版本比较 ## HTTP/1.0 与 HTTP/1.1 的区别 -HTTP/1.1 新增了以下内容: +1. 持久连接 +2. 管线化处理 +3. 虚拟主机 +4. 状态码 100 +5. 分块传输编码 +6. 缓存处理字段 -- 默认为长连接; -- 提供了范围请求功能; -- 提供了虚拟主机的功能; -- 多了一些缓存处理字段; -- 多了一些状态码。 +具体内容见上文 ## HTTP/1.1 与 HTTP/2.0 的区别 @@ -709,9 +827,22 @@ HTTP/1.1 的解析是基于文本的,而 HTTP/2.0 采用二进制格式。 # 参考资料 -- [图解 HTTP](https://pan.baidu.com/s/1M0AHXqG9sP9Bxne6u0JK8A) +- 上野宣. 图解 HTTP[M]. Ren min you dian chu ban she, 2014. - [MDN : HTTP](https://developer.mozilla.org/en-US/docs/Web/HTTP) +- [Are http:// and www really necessary?](https://www.webdancers.com/are-http-and-www-necesary/) +- [HTTP (HyperText Transfer Protocol)](https://www.ntu.edu.sg/home/ehchua/programming/webprogramming/HTTP_Basics.html) +- [Web-VPN: Secure Proxies with SPDY & Chrome](https://www.igvita.com/2011/12/01/web-vpn-secure-proxies-with-spdy-chrome/) +- [File:HTTP persistent connection.svg](http://en.wikipedia.org/wiki/File:HTTP_persistent_connection.svg) +- [Proxy server](https://en.wikipedia.org/wiki/Proxy_server) +- [What Is This HTTPS/SSL Thing And Why Should You Care?](https://www.x-cart.com/blog/what-is-https-and-ssl.html) +- [What is SSL Offloading?](https://securebox.comodo.com/ssl-sniffing/ssl-offloading/) +- [Sun Directory Server Enterprise Edition 7.0 Reference - Key Encryption](https://docs.oracle.com/cd/E19424-01/820-4811/6ng8i26bn/index.html) +- [An Introduction to Mutual SSL Authentication](https://www.codeproject.com/Articles/326574/An-Introduction-to-Mutual-SSL-Authentication) +- [The Difference Between URLs and URIs](https://danielmiessler.com/study/url-uri/) - [维基百科:跨站脚本](https://zh.wikipedia.org/wiki/%E8%B7%A8%E7%B6%B2%E7%AB%99%E6%8C%87%E4%BB%A4%E7%A2%BC) - [维基百科:SQL 注入攻击](https://zh.wikipedia.org/wiki/SQL%E8%B3%87%E6%96%99%E9%9A%B1%E7%A2%BC%E6%94%BB%E6%93%8A) - [维基百科:跨站点请求伪造](https://zh.wikipedia.org/wiki/%E8%B7%A8%E7%AB%99%E8%AF%B7%E6%B1%82%E4%BC%AA%E9%80%A0) - [维基百科:拒绝服务攻击](https://zh.wikipedia.org/wiki/%E9%98%BB%E6%96%B7%E6%9C%8D%E5%8B%99%E6%94%BB%E6%93%8A) +- [What is the difference between a URI, a URL and a URN?](https://stackoverflow.com/questions/176264/what-is-the-difference-between-a-uri-a-url-and-a-urn) +- [XMLHttpRequest](https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest) +- [XMLHttpRequest (XHR) Uses Multiple Packets for HTTP POST?](https://blog.josephscott.org/2009/08/27/xmlhttprequest-xhr-uses-multiple-packets-for-http-post/) diff --git a/notes/JDK 中的设计模式.md b/notes/JDK 中的设计模式.md index daaa90ae..8e2161cc 100644 --- a/notes/JDK 中的设计模式.md +++ b/notes/JDK 中的设计模式.md @@ -101,7 +101,7 @@ java.lang.Cloneable ## 1. 责任链 -避免将请求的发送者附加到其接受者,从而使其它对象也可以处理请求;将请求以对象的方式把发送到链上直到请求被处理完毕。 +避免将请求的发送者附加到其接收者,从而使其它对象也可以处理请求;将请求以对象的方式发送到链上直到请求被处理完毕。 ```java java.util.logging.Logger#log() @@ -253,7 +253,7 @@ java.util.Collections#checked[List|Map|Set|SortedSet|SortedMap]() ## 5. 蝇量模式 -利用共享的方式来支持大量的对象,这些对象一部分内部状态时相同的,而另一份状态可以变化。 +利用共享的方式来支持大量的对象,这些对象一部分内部状态是相同的,而另一份状态可以变化。 Java 利用缓存来加速大量小对象的访问时间。 @@ -268,7 +268,7 @@ java.lang.Character#valueOf(char) 提供一个占位符来控制对象的访问。 -代理可以是一些轻量级的对象,它控制者对重量级对象的访问,只有在真正实例化这些重量级对象时才会去实例化它。 +代理可以是一些轻量级的对象,它控制着对重量级对象的访问,只有在真正实例化这些重量级对象时才会去实例化它。 ```java java.lang.reflect.Proxy diff --git a/notes/Java IO.md b/notes/Java IO.md index 6e91761b..1c1e4e1b 100644 --- a/notes/Java IO.md +++ b/notes/Java IO.md @@ -392,7 +392,7 @@ MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_WRITE, 0, 1024); NIO 与普通 I/O 的区别主要有以下两点: - NIO 是非阻塞的。应当注意,FileChannel 不能切换到非阻塞模式,套接字 Channel 可以。 -- NIO 面向流,I/O 面向块。 +- NIO 面向块,I/O 面向流。 # 八、参考资料 diff --git a/notes/Java 基础.md b/notes/Java 基础.md index f2cc4eb6..6cc8a936 100644 --- a/notes/Java 基础.md +++ b/notes/Java 基础.md @@ -199,7 +199,7 @@ try { java.lang.CloneNotSupportedException: CloneTest ``` -以上抛出了 CloneNotSupportedException,这是因为 CloneTest 没有实现 Cloneable 接口。应该注意的是,clone() 方法并不是 Cloneable 接口的方法,而是 Object 的一个 protect 方法。Cloneable 接口只是规定,如果一个类没有实现 Cloneable 接口又调用了 clone() 方法,就会抛出 CloneNotSupportedException。 +以上抛出了 CloneNotSupportedException,这是因为 CloneTest 没有实现 Cloneable 接口。应该注意的是,clone() 方法并不是 Cloneable 接口的方法,而是 Object 的一个 protected 方法。Cloneable 接口只是规定,如果一个类没有实现 Cloneable 接口又调用了 clone() 方法,就会抛出 CloneNotSupportedException。 **2. 深拷贝与浅拷贝** @@ -235,7 +235,7 @@ Java 中有三个访问权限修饰符:private、protected 以及 public,如 可以对类或类中的成员(字段以及方法)加上访问修饰符。 -- 成员可见表示其它类可以用成员所在类的对象访问到该成员; +- 成员可见表示其它类可该类的对象访问到该成员; - 类可见表示其它类可以用这个类创建对象。 在理解类的可见性时,可以把类当做包中的一个成员,然后包表示一个类,那么就可以类比成员的可见性。 @@ -503,13 +503,13 @@ public static void main(java.lang.String[]); 每个类都有一个 **Class** 对象,包含了与类有关的信息。当编译一个新类时,会产生一个同名的 .class 文件,该文件内容保存着 Class 对象。 -类加载相当于 Class 对象的加载。类在第一次使用时才动态加载到 JVM 中,可以使用 Class.forName('com.mysql.jdbc.Driver.class') 这种方式来控制类的加载,该方法会返回一个 Class 对象。 +类加载相当于 Class 对象的加载。类在第一次使用时才动态加载到 JVM 中,可以使用 Class.forName("com.mysql.jdbc.Driver") 这种方式来控制类的加载,该方法会返回一个 Class 对象。 反射可以提供运行时的类信息,并且这个类可以在运行时才加载进来,甚至在编译时期该类的 .class 不存在也可以加载进来。 Class 和 java.lang.reflect 一起对反射提供了支持,java.lang.reflect 类库主要包含了以下三个类: -1. **Field** :可以使用 get() 和 set() 方法读取和修改 Field 对象关联的字; +1. **Field** :可以使用 get() 和 set() 方法读取和修改 Field 对象关联的字段; 2. **Method** :可以使用 invoke() 方法调用与 Method 对象关联的方法; 3. **Constructor** :可以用 Constructor 创建新的对象。 @@ -535,7 +535,7 @@ Reflection is powerful, but should not be used indiscriminately. If it is possib # 八、异常 -Throwable 可以用来表示任何可以作为异常抛出的类,分为两种: **Error** 和 **Exception**,其中 Error 用来表示编译时系统错误。 +Throwable 可以用来表示任何可以作为异常抛出的类,分为两种: **Error** 和 **Exception**,其中 Error 用来表示 JVM 无法处理的错误(比如 java.lang.OutOfMemoryError)。 Exception 分为两种: diff --git a/notes/Java 容器.md b/notes/Java 容器.md index bb0a9265..2a05225b 100644 --- a/notes/Java 容器.md +++ b/notes/Java 容器.md @@ -87,9 +87,21 @@ for (String item : list) { java.util.Arrays#asList() 可以把数组类型转换为 List 类型。 ```java - List list = Arrays.asList(1, 2, 3); - int[] arr = {1, 2, 3}; - list = Arrays.asList(arr); +@SafeVarargs +public static List asList(T... a) +``` + +如果要将数组类型转换为 List 类型,应该注意的是参数列表为泛型的变长参数,因此不能使用基本类型数组作为参数,只能使用相应的包装类型数组。 + +```java +Integer[] arr = {1, 2, 3}; +List list = Arrays.asList(arr); +``` + +也可以使用以下方式生成 List。 + +```java +List list = Arrays.asList(1,2,3); ``` > [适配器模式](https://github.com/CyC2018/Interview-Notebook/blob/master/notes/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F.md#%E5%8D%81%E9%80%82%E9%85%8D%E5%99%A8%E6%A8%A1%E5%BC%8F) @@ -159,7 +171,7 @@ public class ArrayList extends AbstractList implements List, RandomAccess, Cloneable, java.io.Serializable ``` -基于数组实现,保存元素的数组使用 transient 修饰,该关键字声明数组默认不会被序列化。这是 ArrayList 具有动态扩容特性,因此保存元素的数组不一定都会被使用,那么就没必要全部进行序列化。ArrayList 重写了 writeObject() 和 readObject() 来控制只序列化数组中有元素填充那么部分内容。 +基于数组实现,保存元素的数组使用 transient 修饰,该关键字声明数组默认不会被序列化。这是 ArrayList 具有动态扩容特性,因此保存元素的数组不一定都会被使用,那么就没必要全部进行序列化。ArrayList 重写了 writeObject() 和 readObject() 来控制只序列化数组中有元素填充那部分内容。 ```java transient Object[] elementData; // non-private to simplify nested class access @@ -743,7 +755,7 @@ JDK 1.8 的实现不是用了 Segment,Segment 属于重入锁 ReentrantLock。 # 五、参考资料 -- Java 编程思想 +- Eckel B. Java 编程思想 [M]. 机械工业出版社, 2002. - [Java Collection Framework](https://www.w3resource.com/java-tutorial/java-collections.php) - [Iterator 模式](https://openhome.cc/Gossip/DesignPattern/IteratorPattern.htm) - [Java 8 系列之重新认识 HashMap](https://tech.meituan.com/java-hashmap.html) @@ -752,4 +764,5 @@ JDK 1.8 的实现不是用了 Segment,Segment 属于重入锁 ReentrantLock。 - [The principle of ConcurrentHashMap analysis](http://www.programering.com/a/MDO3QDNwATM.html) - [探索 ConcurrentHashMap 高并发性的实现机制](https://www.ibm.com/developerworks/cn/java/java-lo-concurrenthashmap/) - [HashMap 相关面试题及其解答](https://www.jianshu.com/p/75adf47958a7) +- [Java 集合细节(二):asList 的缺陷](http://wiki.jikexueyuan.com/project/java-enhancement/java-thirtysix.html) diff --git a/notes/Java 并发.md b/notes/Java 并发.md index 59813682..f7e64359 100644 --- a/notes/Java 并发.md +++ b/notes/Java 并发.md @@ -18,26 +18,15 @@ * [线程通信](#线程通信) * [五、线程状态转换](#五线程状态转换) * [六、Executor](#六executor) -* [七、volatile](#七volatile) - * [保证内存可见性](#保证内存可见性) - * [禁止指令重排](#禁止指令重排) -* [八、内存模型](#八内存模型) - * [1. 硬件的效率与一致性](#1-硬件的效率与一致性) - * [2. Java 内存模型](#2-java-内存模型) - * [3. 主内存与工作内存](#3-主内存与工作内存) - * [4. 内存间交互操作](#4-内存间交互操作) - * [5. 内存模型三大特性](#5-内存模型三大特性) - * [6. 先行发生原则](#6-先行发生原则) -* [九、线程安全](#九线程安全) - * [1. Java 语言中的线程安全](#1-java-语言中的线程安全) - * [2. 线程安全的实现方法](#2-线程安全的实现方法) -* [十、锁优化](#十锁优化) - * [1. 自旋锁与自适应自旋](#1-自旋锁与自适应自旋) - * [2. 锁消除](#2-锁消除) - * [3. 锁粗化](#3-锁粗化) - * [4. 轻量级锁](#4-轻量级锁) - * [5. 偏向锁](#5-偏向锁) -* [十一、多线程开发良好的实践](#十一多线程开发良好的实践) +* [七、内存模型](#七内存模型) + * [主内存与工作内存](#主内存与工作内存) + * [内存模型三大特性](#内存模型三大特性) + * [先行发生原则](#先行发生原则) +* [八、线程安全](#八线程安全) + * [线程安全分类](#线程安全分类) + * [线程安全的实现方法](#线程安全的实现方法) + * [锁优化](#锁优化) +* [九、多线程开发良好的实践](#九多线程开发良好的实践) * [参考资料](#参考资料) @@ -111,7 +100,7 @@ public class MyThread extends Thread { 实现接口会更好一些,因为: 1. Java 不支持多重继承,因此继承了 Thread 类就无法继承其它类,但是可以实现多个接口; -2. 类可能只要求可执行即可,继承整个 Thread 类开销会过大。 +2. 类可能只要求可执行就行,继承整个 Thread 类开销会过大。 # 二、基础线程机制 @@ -167,11 +156,19 @@ main() 属于非后台线程。 一个线程进入阻塞状态可能有以下原因: -1. 调用 Thread.sleep() 方法进入休眠状态; -2. 通过 wait() 使线程挂起,直到线程得到 notify() 或 notifyAll() 消息(或者 java.util.concurrent 类库中等价的 signal() 或 signalAll() 消息; +1. 调用 Thread.sleep() 使线程睡眠; +2. 调用 wait() 使线程挂起,直到线程得到 notify() 或 notifyAll() 消息(或者 java.util.concurrent 类库中等价的 signal() 或 signalAll() 消息; 3. 等待某个 I/O 的完成; 4. 试图在某个对象上调用其同步控制方法,但是对象锁不可用,因为另一个线程已经获得了这个锁。 +**阻塞 睡眠 挂起** + +阻塞是一种状态,而睡眠和挂起是一种手段,通过睡眠和挂起可以让一个线程进入阻塞状态。 + +睡眠和挂起这两种手段的区别是,挂起手段会释放对象锁,而阻塞手段不会。 + +应该注意的是,睡眠和挂起都可以设置一个等待时间,超过等待时间之后,线程会退出阻塞状态。但是如果不为挂起设置等待时间,那么它只能等到通知的到来才能退出阻塞状态。 + ## 中断 使用中断机制即可终止阻塞的线程。 @@ -186,7 +183,7 @@ main() 属于非后台线程。 **2. Executor 的中断操作** -Executor 避免对 Thread 对象的直接操作,但是使用 interrupt() 方法必须持有 Thread 对象。Executor 使用 shutdownNow() 方法来中断它里面的所有线程,shutdownNow() 方法会发送 interrupt() 调用给所有线程。 +Executor 避免对 Thread 对象的直接操作,使用 shutdownNow() 方法来中断它里面的所有线程,shutdownNow() 方法会发送 interrupt() 调用给所有线程。 如果只想中断一个线程,那么使用 Executor 的 submit() 而不是 executor() 来启动线程,就可以持有线程的上下文。submit() 将返回一个泛型 Futrue,可以在它之上调用 cancel(),如果将 true 传递给 cancel(),那么它将会发送 interrupt() 调用给特定的线程。 @@ -194,7 +191,7 @@ Executor 避免对 Thread 对象的直接操作,但是使用 interrupt() 方 通过中断的方法来终止线程,需要线程进入阻塞状态才能终止。如果编写的 run() 方法循环条件为 true,但是该线程不发生阻塞,那么线程就永远无法终止。 -interrupt() 方法会设置中断状态,可以通过 interrupted() 方法来检查中断状,从而判断一个线程是否已经被中断。 +interrupt() 方法会设置中断状态,可以通过 interrupted() 方法来检查中断状态,从而判断一个线程是否已经被中断。 interrupted() 方法在检查完中断状态之后会清除中断状态,这样做是为了确保一次中断操作只会产生一次影响。 @@ -212,7 +209,7 @@ interrupted() 方法在检查完中断状态之后会清除中断状态,这样 而同步又可以保证互斥。即进程按一定顺序执行,可以保证在同一时刻只有一个进程能访问临界资源。但是同步不止用来实现互斥,例如生成者消费者问题,生产者和消费者进程之间的同步不是用来控制对临界资源的访问。 -总结起来就是:通信 --> 同步 --> 互斥。 +总结起来就是:通信 -> 同步 -> 互斥。 进程和线程在一定程度上类似,也可以用这些概念来描述。 @@ -221,6 +218,8 @@ interrupted() 方法在检查完中断状态之后会清除中断状态,这样 1. 同步:可以和操作系统的互斥等同; 2. 通信:可以和操作系统的同步等同。 +很多时候这三个概念都会混在一起用,不同的文章有不同的解释,不能说哪个是对的哪个是错的,只要自己能理解就行。 + ## 线程同步 给定一个进程内的所有线程,都共享同一存储空间,这样有好处又有坏处。这些线程就可以共享数据,非常有用。不过,在两个线程同时修改某一资源时,这也会造成一些问题。Java 提供了同步机制,以控制对共享资源的互斥访问。 @@ -247,9 +246,9 @@ public void func(String name) { } ``` -### 2. Lock +### 2. ReentrantLock -实现更细粒度的控制。 +可以使用 Lock 来对一个语句块进行同步。 ```java private Lock lock; @@ -263,17 +262,33 @@ public int func(int value) { } ``` +ReentrantLock 是 java.util.concurrent(J.U.C)包中的锁,相比于 synchronized,它多了一些高级功能: + +**等待可中断** + +当持有锁的线程长期不释放锁的时候,正在等待的线程可以选择放弃等待,改为处理其他事情,可中断特性对处理执行时间非常长的同步块很有帮助。 + +**可实现公平锁** + +公平锁是指多个线程在等待同一个锁时,必须按照申请锁的时间顺序来依次获得锁;而非公平锁则不保证这一点,在锁被释放时,任何一个等待锁的线程都有机会获得锁。synchronized 中的锁是非公平的,ReentrantLock 默认情况下也是非公平的,但可以通过带布尔值的构造函数要求使用公平锁。 + +**锁绑定多个条件** + +一个 ReentrantLock 对象可以同时绑定多个 Condition 对象,而在 synchronized 中,锁对象的 wait() 和 notify() 或 notifyAll() 方法可以实现一个隐含的条件,如果要和多于一个的条件关联的时候,就不得不额外地添加一个锁,而 ReentrantLock 则无须这样做,只需要多次调用 newCondition() 方法即可。 + +如果需要使用上述功能,选用 ReentrantLock 是一个很好的选择。从性能上来看,在新版本的 JDK 中对 synchronized 进行了很多优化,例如自旋锁等。目前来看它和 ReentrantLock 的性能基本持平了,因此性能因素不再是选择 ReentrantLock 的理由,而且 synchronized 有更大的优化空间,因此优先考虑 synchronized。 + ## 线程通信 ### 1. wait() notify() notifyAll() 它们都属于 Object 的一部分,而不属于 Thread。 -wait() 会在等待时将线程挂起,而不是忙等待,并且只有在 notify() 或者 notifyAll() 到达时才唤醒。 +wait() 会在等待时将线程挂起,而不是忙等待,并且只有在 notify() 或者 notifyAll() 到达时才唤醒。可以通过这种机制让一个线程阻塞,直到某种特定条件满足。 sleep() 和 yield() 并没有释放锁,但是 wait() 会释放锁。 -实际上,只有在同步控制方法或同步控制块里才能调用 wait() 、notify() 和 notifyAll()。 +只有在同步控制方法或同步控制块里才能调用 wait() 、notify() 和 notifyAll()。 ```java private boolean flag = false; @@ -293,8 +308,10 @@ public synchronized void before() { **wait() 和 sleep() 的区别** +这两种方法都能将线程阻塞,一种是使用挂起的方式,一种使用睡眠的方式。 + 1. wait() 是 Object 类的方法,而 sleep() 是 Thread 的静态方法; -2. wait() 会放弃锁,而 sleep() 不会。 +2. 挂起会释放锁,睡眠不会。 ### 2. BlockingQueue @@ -303,16 +320,16 @@ java.util.concurrent.BlockingQueue 接口有以下阻塞队列的实现: - **FIFO 队列** :LinkedBlockingQueue、ArrayListBlockingQueue(固定长度) - **优先级队列** :PriorityBlockingQueue -提供了阻塞的 take() 和 put() 方法:如果队列为空 take() 将一直阻塞到队列中有内容,如果队列为满 put() 将阻塞到队列有空闲位置。它们响应中断,当收到中断请求的时候会抛出 InterruptedException,从而提前结束阻塞状态。 +提供了阻塞的 take() 和 put() 方法:如果队列为空 take() 将阻塞,直到队列中有内容;如果队列为满 put() 将阻塞,指到队列有空闲位置。 -阻塞队列的 take() 和 put() 方法是线程安全的。 +它们响应中断,当收到中断请求的时候会抛出 InterruptedException,从而提前结束阻塞状态。 + +是线程安全的。 **使用 BlockingQueue 实现生产者消费者问题** ```java // 生产者 -import java.util.concurrent.BlockingQueue; - public class Producer implements Runnable { private BlockingQueue queue; @@ -322,8 +339,8 @@ public class Producer implements Runnable { @Override public void run() { - System.out.println(Thread.currentThread().getName() + " is making product..."); - String product = "made by " + Thread.currentThread().getName(); + System.out.println(Thread.currentThread().getName() + " is making product."); + String product = "Made By " + Thread.currentThread().getName(); try { queue.put(product); } catch (InterruptedException e) { @@ -335,8 +352,6 @@ public class Producer implements Runnable { ```java // 消费者 -import java.util.concurrent.BlockingQueue; - public class Consumer implements Runnable { private BlockingQueue queue; @@ -347,8 +362,8 @@ public class Consumer implements Runnable { @Override public void run() { try { - String product = queue.take(); - System.out.println(Thread.currentThread().getName() + " is consuming product " + product + "..."); + String product = queue.take(); + System.out.println(Thread.currentThread().getName() + " is consuming product." + "( " + product + " )"); } catch (InterruptedException e) { e.printStackTrace(); } @@ -358,21 +373,18 @@ public class Consumer implements Runnable { ```java // 客户端 -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; - public class Client { public static void main(String[] args) { BlockingQueue queue = new LinkedBlockingQueue<>(5); for (int i = 0; i < 2; i++) { - new Thread(new Consumer(queue), "Consumer" + i).start(); + new Thread(new Consumer(queue), "Consumer-" + i).start(); } for (int i = 0; i < 5; i++) { // 只有两个 Product,因此只能消费两个,其它三个消费者被阻塞 - new Thread(new Producer(queue), "Producer" + i).start(); + new Thread(new Producer(queue), "Producer-" + i).start(); } for (int i = 2; i < 5; i++) { - new Thread(new Consumer(queue), "Consumer" + i).start(); + new Thread(new Consumer(queue), "Consumer-" + i).start(); } } } @@ -380,16 +392,16 @@ public class Client { ```html // 运行结果 -Producer0 is making product... -Consumer0 is consuming product made by Consumer0... -Producer1 is making product... -Consumer1 is consuming product made by Consumer1... -Producer2 is making product... -Producer3 is making product... -Producer4 is making product... -Consumer2 is consuming product made by Consumer2... -Consumer3 is consuming product made by Consumer3... -Consumer4 is consuming product made by Consumer4... +Producer-0 is making product. +Consumer-0 is consuming product.( Made By Producer-0 ) +Producer-1 is making product. +Consumer-1 is consuming product.( Made By Producer-1 ) +Producer-2 is making product. +Producer-3 is making product. +Producer-4 is making product. +Consumer-2 is consuming product.( Made By Producer-2 ) +Consumer-3 is consuming product.( Made By Producer-3 ) +Consumer-4 is consuming product.( Made By Producer-4 ) ``` # 五、线程状态转换 @@ -399,9 +411,9 @@ Consumer4 is consuming product made by Consumer4... 1. 新建(New):创建后尚未启动; 2. 可运行(Runnale):可能正在运行,也可能正在等待 CPU 时间片; 3. 无限期等待(Waiting):等待其它线程显示地唤醒,否则不会被分配 CPU 时间片; -4. 限期等待(Timed Waiting):无序等待其它线程显示地唤醒,在一定时间之后会被系统自动唤醒; +4. 限期等待(Timed Waiting):无需等待其它线程显示地唤醒,在一定时间之后会被系统自动唤醒; 5. 阻塞(Blocking):等待获取一个排它锁,如果其线程释放了锁就会结束此状态; -6. 死亡(Terminated) +6. 死亡(Terminated):可以是线程结束任务之后自己结束,或者产生了异常而结束,中断机制就是使用了抛出中断异常的方式让一个阻塞的线程结束。 # 六、Executor @@ -420,70 +432,21 @@ for(int i = 0; i < 5; i++) { } ``` -# 七、volatile +# 七、内存模型 -保证了内存可见性和禁止指令重排,没法保证原子性。 - -## 保证内存可见性 - -普通共享变量被修改之后,什么时候被写入主存是不确定的。 - -volatile 关键字会保证每次修改共享变量之后该值会立即更新到内存中,并且在读取时会从内存中读取值。 - -synchronized 和 Lock 也能够保证内存可见性。它们能保证同一时刻只有一个线程获取锁然后执行同步代码,并且在释放锁之前会将对变量的修改刷新到主存当中。不过只有对共享变量的 set() 和 get() 方法都加上 synchronized 才能保证可见性,如果只有 set() 方法加了 synchronized,那么 get() 方法并不能保证会从内存中读取最新的数据。 - -## 禁止指令重排 - -在 Java 内存模型中,允许编译器和处理器对指令进行重排序,重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性。 - -volatile 关键字通过添加内存屏障的方式来进制指令重排,即重排序时不能把后面的指令放到内存屏障之前。 - -可以通过 synchronized 和 Lock 来保证有序性,它们保证每个时刻只有一个线程执行同步代码,相当于是让线程顺序执行同步代码,自然就保证了有序性。 - -# 八、内存模型 - -## 1. 硬件的效率与一致性 +## 主内存与工作内存 对处理器上的寄存器进行读写的速度比内存快几个数量级,为了解决这种速度矛盾,在它们之间加入了高速缓存。 -每个处理器都有一个高速缓存,但是所有处理器共用一个主内存,因此高速缓存引入了一个新问题:缓存一致性。当多个处理器的运算都涉及同一块主内存区域时,将可能导致各自的缓存数据不一致。缓存不一致问题通常需要使用一些协议来解决。 +所有的变量都存储在主内存中,每个线程还有自己的工作内存,工作内存存储在高速缓存中,保存了被该线程使用到的变量的主内存副本拷贝,线程只能直接操作工作内存中的变量。 -

+

-除了增加高速缓存之外,为了使得处理器内部的运算单元能尽量被充分利用,处理器可能会对输入代码进行乱序执行(Out-Of-Order Execution)优化,处理器会在计算之后将乱序执行的结果重组,保证该结果与顺序执行的结果是一致的,但并不保证程序中各个语句计算的先后顺序与输入代码中的顺序一致,因此,如果存在一个计算任务依赖另外一个计算任务的中间结果,那么其顺序性并不能靠代码的先后顺序来保证。与处理器的乱序执行优化类似,Java 虚拟机的即时编译器中也有类似的指令重排序(Instruction Reorder)优化。 +## 内存模型三大特性 -## 2. Java 内存模型 +### 1. 原子性 -Java 虚拟机规范中试图定义一种 Java 内存模型来屏蔽掉各种硬件和操作系统的内存访问差异,以实现让 Java 程序在各种平台下都能达到一致的内存访问效果。在此之前,主流程序语言(如 C/C++等)直接使用物理硬件和操作系统的内存模型,但由于不同平台上内存模型的差异,有可能导致程序在一套平台上并发完全正常,而在另外一套平台上并发访问却经常出错,因此在某些场景就必须针对不同的平台来编写程序。 - -## 3. 主内存与工作内存 - -Java 内存模型的主要目标是定义程序中各个变量的访问规则,即在虚拟机中将变量存储到内存和从内存中取出变量这样的底层细节。此处的变量(Variables)与 Java 编程中所说的变量有所区别,它包括了实例字段、静态字段和构成数组对象的元素,但不包括局部变量与方法参数,因为后者是线程私有的,不会被共享,自然就不会存在竞争问题。 - -Java 内存模型规定了所有的变量都存储在主内存(Main Memory)中。每条线程还有自己的工作内存,线程的工作内存中保存了被该线程使用到的变量的主内存副本拷贝,线程对变量的所有操作(读取、赋值等)都必须在工作内存中进行,而不能直接读写主内存中的变量。不同的线程之间也无法直接访问对方工作内存中的变量,线程间变量值的传递均需要通过主内存来完成,线程、主内存、工作内存三者的交互关系如图所示。 - -

- -## 4. 内存间交互操作 - -Java 内存模型定义了 8 种操作来完成工作内存与主内存之间的交互:一个变量从主内存拷贝到工作内存、从工作内存同步回主内存。虚拟机实现时必须保证下面提及的每一种操作都是原子的、不可再分的。 - -- lock(锁定):作用于主内存的变量,它把一个变量标识为一条线程独占的状态。 -- unlock(解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定。 -- read(读取):作用于主内存的变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的 load 动作使用。 -- load(载入):作用于工作内存的变量,它把 read 操作从主内存中得到的变量值放入工作内存的变量副本中。 -- use(使用):作用于工作内存的变量,它把工作内存中一个变量的值传递给执行引擎,每当虚拟机遇到一个需要使用到变量的值的字节码指令时将会执行这个操作。 -- assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。 -- store(存储):作用于工作内存的变量,它把工作内存中一个变量的值传送到主内存中,以便随后的 write 操作使用。 -- write(写入):作用于主内存的变量,它把 store 操作从工作内存中得到的变量的值放入主内存的变量中。 - -## 5. 内存模型三大特性 - -### 5.1 原子性 - -除了 long 和 double 之外的基本数据类型的访问读写是具备原子性的。 - -Java 内存模型允许虚拟机将没有被 volatile 修饰的 64 位数据的读写操作划分为两次 32 位的操作来进行,即虚拟机可以不保证 64 位数据类型的 load、store、read 和 write 这 4 个操作的原子性。但是目前各种平台下的商用虚拟机几乎都选择把 64 位数据的读写操作作为原子操作来对待。 +Java 内存模型允许虚拟机将没有被 volatile 修饰的 64 位数据(long,double)的读写操作划分为两次 32 位的操作来进行,也就是说对这部分数据的操作可以不具备原子性。 AtomicInteger、AtomicLong、AtomicReference 等特殊的原子性变量类提供了下面形式的原子性条件更新语句,使得比较和更新这两个操作能够不可分割地执行。 @@ -501,106 +464,110 @@ public int next() { } ``` -如果应用场景需要一个更大范围的原子性保证,Java 内存模型还提供了 lock 和 unlock 操作来满足这种需求,尽管虚拟机未把 lock 和 unlock 操作直接开放给用户使用,但是却提供了更高层次的字节码指令 monitorenter 和 monitorexit 来隐式地使用这两个操作,这两个字节码指令反映到 Java 代码中就是同步块——synchronized 关键字,因此在 synchronized 块之间的操作也具备原子性。 +也可以使用 synchronized 同步操作来保证操作具备原子性,它对应的虚拟机字节码指令为 monitorenter 和 monitorexit。 -### 5.2 可见性 +### 2. 可见性 -可见性是指当一个线程修改了共享变量的值,其他线程能立即得知这个修改。 +如果没有及时地对主内存与工作内存的数据进行同步,那么就会出现不一致问题。如果存在不一致的问题,一个线程对一个共享数据所做的修改就不能被另一个线程看到。 -Java 内存模型是通过在变量修改后将新值同步回主内存,在变量读取前从主内存刷新变量值这种依赖主内存作为传递媒介的方式来实现可见性的,无论是普通变量还是 volatile 变量都是如此,普通变量与 volatile 变量的区别是,volatile 的特殊规则保证了新值能立即同步到主内存,以及每次使用前立即从主内存刷新。因此,可以说 volatile 保证了多线程操作时变量的可见性,而普通变量则不能保证这一点。 +volatile 可以保证可见性,它在修改一个共享数据时会将该值从工作内存同步到主内存,并且对一个共享数据进行读取时会先从主内存同步到工作内存。 -除了 volatile 之外,Java 还有两个关键字能实现可见性,即 synchronized 和 final。同步块的可见性是由“对一个变量执行 unlock 操作之前,必须先把此变量同步回主内存中(执行 store、write 操作)”这条规则获得的,而 final 关键字的可见性是指:被 final 修饰的字段在构造器中一旦初始化完成,并且构造器没有把“this”的引用传递出去(this 引用逃逸是一件很危险的事情,其他线程有可能通过这个引用访问到“初始化了一半”的对象),那在其他线程中就能看见 final 字段的值。 +synchronized 也能够保证可见性,他能保证同一时刻只有一个线程获取锁然后执行同步代码,并且在释放锁之前会将对变量的修改刷新到主内存当中。不过只有对共享变量的 set() 和 get() 方法都加上 synchronized 才能保证可见性,如果只有 set() 方法加了 synchronized,那么 get() 方法并不能保证会从内存中读取最新的数据。 -### 5.3 有序性 +### 3. 有序性 -本线程内观察,所有的操作都是有序的;如果在一个线程中观察另一个线程,所有的操作都是无序的。前半句是指线程内表现为串行的语义,后半句是指指令重排和工作内存和主内存存在同步延迟的现象。 +在 Java 内存模型中,允许编译器和处理器对指令进行重排序,重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性。 -Java 语言提供了 volatile 和 synchronized 两个关键字来保证线程之间操作的有序性,volatile 关键字本身就包含了禁止指令重排序的语义,而 synchronized 则是由“一个变量在同一个时刻只允许一条线程对其进行 lock 操作”这条规则获得的,这条规则决定了持有同一个锁的两个同步块只能串行地进入。 +volatile 关键字通过添加内存屏障的方式来禁止指令重排,即重排序时不能把后面的指令放到内存屏障之前。 -synchronized 关键字在需要这 3 种特性的时候都可以作为其中一种的解决方案,看起来很“万能”。的确,大部分的并发控制操作都能使用 synchronized 来完成。synchronized 的“万能”也间接造就了它被程序员滥用的局面,越“万能”的并发控制,通常会伴随着越大的性能影响。 +也可以通过 synchronized 来保证有序性,它保证每个时刻只有一个线程执行同步代码,相当于是让线程顺序执行同步代码,自然就保证了有序性。 -## 6. 先行发生原则 +## 先行发生原则 -如果 Java 内存模型中所有的有序性都只靠 volatile 和 synchronized 来完成,那么有一些操作将会变得很繁琐,但是我们在编写 Java 并发代码的时候并没有感觉到这一点,这是因为 Java 语言中有一个“先行发生”(Happen-Before) 的原则。这个原则非常重要,它是判断数据是否存在竞争,线程是否安全的主要依据。依靠这个原则,我们可以通过几条规则一次性地解决并发环境下两个操作之间是否可能存在冲突的所有问题。 +上面提到了可以用 volatile 和 synchronized 来保证有序性。除此之外,JVM 还规定了先行发生原则,让一个操作无需控制就能先于另一个操作完成。 -先行发生是 Java 内存模型中定义的两项操作之间的偏序关系,如果说操作 A 先行发生于操作 B,其实就是说在发生操作 B 之前,操作 A 产生的影响能被操作 B 观察到,“影响”包括修改了内存中共享变量的值、发送了消息、调用了方法等。 +主要有以下这些原则: -```java -// 以下操作在线程 A 中执行 -k = 1; -// 以下操作在线程 B 中执行 -j = k; -// 以下操作在线程 C 中执行 -k = 2; -``` +### 1. 单一线程原则 -假设线程 A 中的操作“k=1”先行发生于线程 B 的操作“j=k”,那么可以确定在线程 B 的操作执行后,变量 j 的值一定等于 1,得出这个结论的依据有两个:一是根据先行发生原则,“k=1”的结果可以被观察到;二是线程 C 还没“登场”,线程 A 操作结束之后没有其他线程会修改变量 k 的值。现在再来考虑线程 C,我们依然保持线程 A 和线程 B 之间的先行发生关系,而线程 C 出现在线程 A 和线程 B 的操作之间,但是线程 C 与线程 B 没有先行发生关系,那 j 的值会是多少呢?答案是不确定!1 和 2 都有可能,因为线程 C 对变量 k 的影响可能会被线程 B 观察到,也可能不会,这时候线程 B 就存在读取到过期数据的风险,不具备多线程安全性。 +> Single thread rule -下面是 Java 内存模型下一些“天然的”先行发生关系,这些先行发生关系无须任何同步器协助就已经存在,可以在编码中直接使用。如果两个操作之间的关系不在此列,并且无法从下列规则推导出来的话,它们就没有顺序性保障,虚拟机可以对它们随意地进行重排序。 +在一个线程内,在程序前面的操作先行发生于后面的操作。 -- 程序次序规则(Program Order Rule):在一个线程内,按照程序代码顺序,书写在前面的操作先行发生于书写在后面的操作。准确地说,应该是控制流顺序而不是程序代码顺序,因为要考虑分支、循环等结构。 -- 管程锁定规则(Monitor Lock Rule):一个 unlock 操作先行发生于后面对同一个锁的 lock 操作。这里必须强调的是同一个锁,而“后面”是指时间上的先后顺序。 -- volatile 变量规则(Volatile Variable Rule):对一个 volatile 变量的写操作先行发生于后面对这个变量的读操作,这里的“后面”同样是指时间上的先后顺序。 -- 线程启动规则(Thread Start Rule):Thread 对象的 start() 方法先行发生于此线程的每一个动作。 -- 线程终止规则(Thread Termination Rule):线程中的所有操作都先行发生于对此线程的终止检测,我们可以通过 Thread.join() 方法结束、Thread.isAlive() 的返回值等手段检测到线程已经终止执行。 -- 线程中断规则(Thread Interruption Rule):对线程 interrupt() 方法的调用先行发生于被中断线程的代码检测到中断事件的发生,可以通过 Thread.interrupted() 方法检测到是否有中断发生。 -- 对象终结规则(Finalizer Rule):一个对象的初始化完成(构造函数执行结束)先行发生于它的 finalize() 方法的开始。 -- 传递性(Transitivity):如果操作 A 先行发生于操作 B,操作 B 先行发生于操作 C,那就可以得出操作 A 先行发生于操作 C 的结论。 +

-```java -private int value = 0; -pubilc void setValue(int value) { - this.value = value; -} -public int getValue() { - return value; -} -``` -上述代码显示的是一组再普通不过的 getter/setter 方法,假设存在线程 A 和 B,线程 A 先(时间上的先后)调用了“setValue(1)”,然后线程 B 调用了同一个对象的“getValue()”,那么线程 B 收到的返回值是什么? +### 2. 管程锁定规则 -我们依次分析一下先行发生原则中的各项规则,由于两个方法分别由线程 A 和线程 B 调用,不在一个线程中,所以程序次序规则在这里不适用;由于没有同步块,自然就不会发生 lock 和 unlock 操作,所以管程锁定规则不适用;由于 value 变量没有被 volatile 关键字修饰,所以 volatile 变量规则不适用;后面的线程启动、终止、中断规则和对象终结规则也和这里完全没有关系。因为没有一个适用的先行发生规则,所以最后一条传递性也无从谈起,因此我们可以判定尽管线程 A 在操作时间上先于线程 B,但是无法确定线程 B 中“getValue()”方法的返回结果,换句话说,这里面的操作不是线程安全的。 +> Monitor Lock Rule -那怎么修复这个问题呢?我们至少有两种比较简单的方案可以选择:要么把 getter/setter 方法都定义为 synchronized 方法,这样就可以套用管程锁定规则;要么把 value 定义为 volatile 变量,由于 setter 方法对 value 的修改不依赖 value 的原值,满足 volatile 关键字使用场景,这样就可以套用 volatile 变量规则来实现先行发生关系。 +一个 unlock 操作先行发生于后面对同一个锁的 lock 操作。 -通过上面的例子,我们可以得出结论:一个操作“时间上的先发生”不代表这个操作会是“先行发生”,那如果一个操作“先行发生”是否就能推导出这个操作必定是“时间上的先发生”呢?很遗憾,这个推论也是不成立的,一个典型的例子就是多次提到的“指令重排序”。 +

-```java -// 以下操作在同一个线程中执行 -int i = 1; -int j = 2; -``` +### 3. volatile 变量规则 -代码清单的两条赋值语句在同一个线程之中,根据程序次序规则,“int i=1”的操作先行发生于“int j=2”,但是“int j=2”的代码完全可能先被处理器执行,这并不影响先行发生原则的正确性,因为我们在这条线程之中没有办法感知到这点。 +> Volatile Variable Rule -上面两个例子综合起来证明了一个结论:时间先后顺序与先行发生原则之间基本没有太大的关系,所以我们衡量并发安全问题的时候不要受到时间顺序的干扰,一切必须以先行发生原则为准。 +对一个 volatile 变量的写操作先行发生于后面对这个变量的读操作。 -# 九、线程安全 +

-《Java Concurrency In Practice》的作者 Brian Goetz 对“线程安全”有一个比较恰当的定义:“当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那这个对象是线程安全的”。 +### 4. 线程启动规则 -这个定义比较严谨,它要求线程安全的代码都必须具备一个特征:代码本身封装了所有必要的正确性保障手段(如互斥同步等),令调用者无须关心多线程的问题,更无须自己采取任何措施来保证多线程的正确调用。这点听起来简单,但其实并不容易做到,在大多数场景中,我们都会将这个定义弱化一些,如果把“调用这个对象的行为”限定为“单次调用”,这个定义的其他描述也能够成立的话,我们就可以称它是线程安全了,为什么要弱化这个定义,现在暂且放下,稍后再详细探讨。 +> Thread Start Rule -## 1. Java 语言中的线程安全 +Thread 对象的 start() 方法先行发生于此线程的每一个动作。 -我们这里讨论的线程安全,就限定于多个线程之间存在共享数据访问这个前提,因为如果一段代码根本不会与其他线程共享数据,那么从线程安全的角度来看,程序是串行执行还是多线程执行对它来说是完全没有区别的。 +

-为了更加深入地理解线程安全,在这里我们可以不把线程安全当做一个非真即假的二元排他选项来看待,按照线程安全的“安全程度”由强至弱来排序,我们可以将 Java 语言中各种操作共享的数据分为以下 5 类:不可变、绝对线程安全、相对线程安全、线程兼容和线程对立。 +### 5. 线程加入规则 -### 1.1 不可变 +> Thread Join Rule -在 Java 语言中(特指 JDK 1.5 以后,即 Java 内存模型被修正之后的 Java 语言),不可变(Immutable)的对象一定是线程安全的,无论是对象的方法实现还是方法的调用者,都不需要再采取任何的线程安全保障措施,只要一个不可变的对象被正确地构建出来(没有发生 this 引用逃逸的情况),那其外部的可见状态永远也不会改变,永远也不会看到它在多个线程之中处于不一致的状态。“不可变”带来的安全性是最简单和最纯粹的。 +join() 方法返回先行发生于 Thread 对象的结束。 -Java 语言中,如果共享数据是一个基本数据类型,那么只要在定义时使用 final 关键字修饰它就可以保证它是不可变的。如果共享数据是一个对象,那就需要保证对象的行为不会对其状态产生任何影响才行,不妨想一想 java.lang.String 类的对象,它是一个典型的不可变对象,我们调用它的 substring()、replace() 和 concat() 这些方法都不会影响它原来的值,只会返回一个新构造的字符串对象。 +

-保证对象行为不影响自己状态的途径有很多种,其中最简单的就是把对象中带有状态的变量都声明为 final,这样在构造函数结束之后,它就是不可变的。 +### 6. 线程中断规则 -在 Java API 中符合不可变要求的类型,除了上面提到的 String 之外,常用的还有枚举类型,以及 java.lang.Number 的部分子类,如 Long 和 Double 等数值包装类型,BigInteger 和 BigDecimal 等大数据类型;但同为 Number 的子类型的原子类 AtomicInteger 和 AtomicLong 则并非不可变的。 +> Thread Interruption Rule -### 1.2 绝对线程安全 +对线程 interrupt() 方法的调用先行发生于被中断线程的代码检测到中断事件的发生,可以通过 Thread.interrupted() 方法检测到是否有中断发生。 -绝对的线程安全完全满足 Brian Goetz 给出的线程安全的定义,这个定义其实是很严格的,一个类要达到“不管运行时环境如何,调用者都不需要任何额外的同步措施”通常需要付出很大的,甚至有时候是不切实际的代价。在 Java API 中标注自己是线程安全的类,大多数都不是绝对的线程安全。我们可以通过 Java API 中一个不是“绝对线程安全”的线程安全类来看看这里的“绝对”是什么意思。 +### 7. 对象终结规则 -如果说 java.util.Vector 是一个线程安全的容器,相信所有的 Java 程序员对此都不会有异议,因为它的 add()、get() 和 size() 这类方法都是被 synchronized 修饰的,尽管这样效率很低,但确实是安全的。但是,即使它所有的方法都被修饰成同步,也不意味着调用它的时候永远都不再需要同步手段了。 +> Finalizer Rule + +一个对象的初始化完成(构造函数执行结束)先行发生于它的 finalize() 方法的开始。 + +### 8. 传递性 + +> Transitivity + +如果操作 A 先行发生于操作 B,操作 B 先行发生于操作 C,那么操作 A 先行发生于操作 C。 + +# 八、线程安全 + +当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那这个对象是线程安全的。 《Java Concurrency In Practice》 + +## 线程安全分类 + +### 1. 不可变 + +不可变(Immutable)的对象一定是线程安全的,无论是对象的方法实现还是方法的调用者,都不需要再采取任何的线程安全保障措施,只要一个不可变的对象被正确地构建出来,那其外部的可见状态永远也不会改变,永远也不会看到它在多个线程之中处于不一致的状态。 + +不可变的类: + +- String +- Number 部分子类,如 Long 和 Double 等数值包装类型,BigInteger 和 BigDecimal 等大数据类型。但同为 Number 的子类型的原子类 AtomicInteger 和 AtomicLong 则并非不可变的。 + +可以使用 final 关键字修饰一个基本数据类型的共享数据,使它具有不可变性。 + +### 2. 绝对线程安全 + +在 Java API 中标注自己是线程安全的类,大多数都不是绝对的线程安全。例如Vector 是一个线程安全的容器,它的方法被 synchronized 被修饰同步。即使是这样,也不意味着调用它的时候永远都不再需要同步手段了。 + +对于下面的代码,在多线程的环境中,如果不在方法调用端做额外的同步措施的话,使用这段代码仍然是不安全的。因为如果另一个线程恰好在错误的时间里删除了一个元素,导致序号 i 已经不再可用的话,再用 i 访问数组就会抛出一个 ArrayIndexOutOfBoundsException。 ```java private static Vector vector = new Vector(); @@ -632,21 +599,20 @@ public static void main(String[] args) { removeThread.start(); printThread.start(); - // 不要同时产生过多的线程,否则会导致操作系统假死 while (Thread.activeCount() > 20); } } ``` ```html -Exception in thread"Thread-132"java.lang.ArrayIndexOutOfBoundsException: +Exception in thread "Thread-132" java.lang.ArrayIndexOutOfBoundsException: Array index out of range:17 at java.util.Vector.remove(Vector.java:777) at org.fenixsoft.mulithread.VectorTest$1.run(VectorTest.java:21) at java.lang.Thread.run(Thread.java:662) ``` -很明显,尽管这里使用到的 Vector 的 get()、remove() 和 size() 方法都是同步的,但是在多线程的环境中,如果不在方法调用端做额外的同步措施的话,使用这段代码仍然是不安全的,因为如果另一个线程恰好在错误的时间里删除了一个元素,导致序号 i 已经不再可用的话,再用 i 访问数组就会抛出一个 ArrayIndexOutOfBoundsException。如果要保证这段代码能正确执行下去,我们不得不把 removeThread 和 printThread 的定义改成如下所示的样子: +如果要保证上面的代码能正确执行下去,就需要对 removeThread 和 printThread 中的方法进行同步。 ```java Thread removeThread = new Thread(new Runnable() { @@ -672,177 +638,80 @@ Thread printThread = new Thread(new Runnable() { }); ``` -### 1.3 相对线程安全 +### 3. 相对线程安全 -相对的线程安全就是我们通常意义上所讲的线程安全,它需要保证对这个对象单独的操作是线程安全的,我们在调用的时候不需要做额外的保障措施,但是对于一些特定顺序的连续调用,就可能需要在调用端使用额外的同步手段来保证调用的正确性。 +相对的线程安全需要保证对这个对象单独的操作是线程安全的,我们在调用的时候不需要做额外的保障措施,但是对于一些特定顺序的连续调用,就可能需要在调用端使用额外的同步手段来保证调用的正确性。 在 Java 语言中,大部分的线程安全类都属于这种类型,例如 Vector、HashTable、Collections 的 synchronizedCollection() 方法包装的集合等。 -### 1.4 线程兼容 +### 4. 线程兼容 线程兼容是指对象本身并不是线程安全的,但是可以通过在调用端正确地使用同步手段来保证对象在并发环境中可以安全地使用,我们平常说一个类不是线程安全的,绝大多数时候指的是这一种情况。Java API 中大部分的类都是属于线程兼容的,如与前面的 Vector 和 HashTable 相对应的集合类 ArrayList 和 HashMap 等。 -### 1.5 线程对立 +### 5. 线程对立 线程对立是指无论调用端是否采取了同步措施,都无法在多线程环境中并发使用的代码。由于 Java 语言天生就具备多线程特性,线程对立这种排斥多线程的代码是很少出现的,而且通常都是有害的,应当尽量避免。 -一个线程对立的例子是 Thread 类的 suspend() 和 resume() 方法,如果有两个线程同时持有一个线程对象,一个尝试去中断线程,另一个尝试去恢复线程,如果并发进行的话,无论调用时是否进行了同步,目标线程都是存在死锁风险的,如果 suspend() 中断的线程就是即将要执行 resume() 的那个线程,那就肯定要产生死锁了。也正是由于这个原因,suspend() 和 resume() 方法已经被 JDK 声明废弃(@Deprecated)了。常见的线程对立的操作还有 System.setIn()、Sytem.setOut() 和 System.runFinalizersOnExit() 等。 +## 线程安全的实现方法 -## 2. 线程安全的实现方法 +如何实现线程安全与代码编写有很大的关系,但虚拟机提供的同步和锁机制也起到了非常重要的作用。 -如何实现线程安全与代码编写有很大的关系,但虚拟机提供的同步和锁机制也起到了非常重要的作用。本节中,代码编写如何实现线程安全和虚拟机如何实现同步与锁这两者都会有所涉及,相对而言更偏重后者一些,只要读者了解了虚拟机线程安全手段的运作过程,自己去思考代码如何编写并不是一件困难的事情。 +### 1. 互斥同步 -### 2.1 互斥同步 +synchronized 和 ReentrantLock。 -互斥同步(Mutual Exclusion&Synchronization)是常见的一种并发正确性保障手段。同步是指在多个线程并发访问共享数据时,保证共享数据在同一个时刻只被一个(或者是一些,使用信号量的时候)线程使用。而互斥是实现同步的一种手段,临界区(Critical Section)、互斥量(Mutex)和信号量(Semaphore)都是主要的互斥实现方式。因此,在这 4 个字里面,互斥是因,同步是果;互斥是方法,同步是目的。 +### 2. 非阻塞同步 -在 Java 中,最基本的互斥同步手段就是 synchronized 关键字,synchronized 关键字经过编译之后,会在同步块的前后分别形成 monitorenter 和 monitorexit 这两个字节码指令,这两个字节码都需要一个 reference 类型的参数来指明要锁定和解锁的对象。如果 Java 程序中的 synchronized 明确指定了对象参数,那就是这个对象的 reference;如果没有明确指定,那就根据 synchronized 修饰的是实例方法还是类方法,去取对应的对象实例或 Class 对象来作为锁对象。 +互斥同步最主要的问题就是进行线程阻塞和唤醒所带来的性能问题,因此这种同步也称为阻塞同步(Blocking Synchronization)。 -根据虚拟机规范的要求,在执行 monitorenter 指令时,首先要尝试获取对象的锁。如果这个对象没被锁定,或者当前线程已经拥有了那个对象的锁,把锁的计数器加 1,相应的,在执行 monitorexit 指令时会将锁计数器减 1,当计数器为 0 时,锁就被释放。如果获取对象锁失败,那当前线程就要阻塞等待,直到对象锁被另外一个线程释放为止。 +从处理问题的方式上说,互斥同步属于一种悲观的并发策略,总是认为只要不去做正确的同步措施(例如加锁),那就肯定会出现问题,无论共享数据是否真的会出现竞争,它都要进行加锁(这里讨论的是概念模型,实际上虚拟机会优化掉很大一部分不必要的加锁)、用户态核心态转换、维护锁计数器和检查是否有被阻塞的线程需要唤醒等操作。随着硬件指令集的发展,我们有了另外一个选择:基于冲突检测的乐观并发策略,通俗地说,就是先进行操作,如果没有其他线程争用共享数据,那操作就成功了;如果共享数据有争用,产生了冲突,那就再采取其他的补偿措施(最常见的补偿措施就是不断地重试,直到成功为止),这种乐观的并发策略的许多实现都不需要把线程挂起,因此这种同步操作称为非阻塞同步(Non-Blocking Synchronization)。 -在虚拟机规范对 monitorenter 和 monitorexit 的行为描述中,有两点是需要特别注意的。首先,synchronized 同步块对同一条线程来说是可重入的,不会出现自己把自己锁死的问题。其次,同步块在已进入的线程执行完之前,会阻塞后面其他线程的进入。Java 的线程是映射到操作系统的原生线程之上的,如果要阻塞或唤醒一个线程,都需要操作系统来帮忙完成,这就需要从用户态转换到核心态中,因此状态转换需要耗费很多的处理器时间。对于代码简单的同步块(如被 synchronized 修饰的 getter() 或 setter() 方法),状态转换消耗的时间有可能比用户代码执行的时间还要长。所以 synchronized 是 Java 语言中一个重量级(Heavyweight)的操作,有经验的程序员都会在确实必要的情况下才使用这种操作。而虚拟机本身也会进行一些优化,譬如在通知操作系统阻塞线程之前加入一段自旋等待过程,避免频繁地切入到核心态之中。 +乐观锁需要操作和冲突检测这两个步骤具备原子性,这里就不能再使用互斥同步来保证了,只能靠硬件来完成。硬件支持的原子性操作最典型的是:比较并交换(Compare-and-Swap,CAS)。 -除了 synchronized 之外,我们还可以使用 java.util.concurrent(下文称 J.U.C)包中的重入锁(ReentrantLock)来实现同步,在基本用法上,ReentrantLock 与 synchronized 很相似,他们都具备一样的线程重入特性,只是代码写法上有点区别,一个表现为 API 层面的互斥锁(lock() 和 unlock() 方法配合 try/finally 语句块来完成),另一个表现为原生语法层面的互斥锁。不过,相比 synchronized,ReentrantLock 增加了一些高级功能,主要有以下 3 项:等待可中断、可实现公平锁,以及锁可以绑定多个条件。 +CAS 指令需要有 3 个操作数,分别是内存位置(在 Java 中可以简单理解为变量的内存地址,用 V 表示)、旧的预期值(用 A 表示)和新值(用 B 表示)。CAS 指令执行时,当且仅当 V 符合旧预期值 A 时,处理器用新值 B 更新 V 的值,否则它就不执行更新。但是无论是否更新了 V 的值,都会返回 V 的旧值,上述的处理过程是一个原子操作。 -- 等待可中断是指当持有锁的线程长期不释放锁的时候,正在等待的线程可以选择放弃等待,改为处理其他事情,可中断特性对处理执行时间非常长的同步块很有帮助。 +J.U.C 包里面的整数原子类 AtomicInteger,其中的 compareAndSet() 和 getAndIncrement() 等方法都使用了 Unsafe 类的 CAS 操作。 -- 公平锁是指多个线程在等待同一个锁时,必须按照申请锁的时间顺序来依次获得锁;而非公平锁则不保证这一点,在锁被释放时,任何一个等待锁的线程都有机会获得锁。synchronized 中的锁是非公平的,ReentrantLock 默认情况下也是非公平的,但可以通过带布尔值的构造函数要求使用公平锁。 +ABA :如果一个变量 V 初次读取的时候是 A 值,它的值被改成了 B,后来又被改回为 A,那 CAS 操作就会误认为它从来没有被改变过。大部分情况下 ABA 问题不会影响程序并发的正确性,如果需要解决 ABA 问题,改用传统的互斥同步可能会比原子类更高效。 -- 锁绑定多个条件是指一个 ReentrantLock 对象可以同时绑定多个 Condition 对象,而在 synchronized 中,锁对象的 wait() 和 notify() 或 notifyAll() 方法可以实现一个隐含的条件,如果要和多于一个的条件关联的时候,就不得不额外地添加一个锁,而 ReentrantLock 则无须这样做,只需要多次调用 newCondition() 方法即可。 +### 3. 无同步方案 -如果需要使用上述功能,选用 ReentrantLock 是一个很好的选择,那如果是基于性能考虑呢?关于 synchronized 和 ReentrantLock 的性能问题,Brian Goetz 对这两种锁在 JDK 1.5 与单核处理器,以及 JDK 1.5 与双 Xeon 处理器环境下做了一组吞吐量对比的实验,实验结果如图 13-1 和图 13-2 所示。 +要保证线程安全,并不是一定就要进行同步,两者没有因果关系。同步只是保证共享数据争用时的正确性的手段,如果一个方法本来就不涉及共享数据,那它自然就无须任何同步措施去保证正确性,因此会有一些代码天生就是线程安全的。 -

-
JDK 1.5、单核处理器下两种锁的吞吐量对比

+**可重入代码(Reentrant Code)** -

-
JDK 1.5、双 Xeon 处理器下两种锁的吞吐量对比

- -多线程环境下 synchronized 的吞吐量下降得非常严重,而 ReentrantLock 则能基本保持在同一个比较稳定的水平上。与其说 ReentrantLock 性能好,还不如说 synchronized 还有非常大的优化余地。后续的技术发展也证明了这一点,JDK 1.6 中加入了很多针对锁的优化措施,JDK 1.6 发布之后,人们就发现 synchronized 与 ReentrantLock 的性能基本上是完全持平了。因此,如果读者的程序是使用 JDK 1.6 或以上部署的话,性能因素就不再是选择 ReentrantLock 的理由了,虚拟机在未来的性能改进中肯定也会更加偏向于原生的 synchronized,所以还是提倡在 synchronized 能实现需求的情况下,优先考虑使用 synchronized 来进行同步。 - -### 2.2 非阻塞同步 - -互斥同步最主要的问题就是进行线程阻塞和唤醒所带来的性能问题,因此这种同步也称为阻塞同步(Blocking Synchronization)。从处理问题的方式上说,互斥同步属于一种悲观的并发策略,总是认为只要不去做正确的同步措施(例如加锁),那就肯定会出现问题,无论共享数据是否真的会出现竞争,它都要进行加锁(这里讨论的是概念模型,实际上虚拟机会优化掉很大一部分不必要的加锁)、用户态核心态转换、维护锁计数器和检查是否有被阻塞的线程需要唤醒等操作。随着硬件指令集的发展,我们有了另外一个选择:基于冲突检测的乐观并发策略,通俗地说,就是先进行操作,如果没有其他线程争用共享数据,那操作就成功了;如果共享数据有争用,产生了冲突,那就再采取其他的补偿措施(最常见的补偿措施就是不断地重试,直到成功为止),这种乐观的并发策略的许多实现都不需要把线程挂起,因此这种同步操作称为非阻塞同步(Non-Blocking Synchronization)。 - -为什么笔者说使用乐观并发策略需要“硬件指令集的发展”才能进行呢?因为我们需要操作和冲突检测这两个步骤具备原子性,靠什么来保证呢?如果这里再使用互斥同步来保证就失去意义了,所以我们只能靠硬件来完成这件事情,硬件保证一个从语义上看起来需要多次操作的行为只通过一条处理器指令就能完成,这类指令常用的有: - -- 测试并设置(Test-and-Set) -- 获取并增加(Fetch-and-Increment) -- 交换(Swap) -- 比较并交换(Compare-and-Swap,下文称 CAS) -- 加载链接/条件存储(Load-Linked/Store-Conditional,下文称 LL/SC) - -其中,前面的 3 条是 20 世纪就已经存在于大多数指令集之中的处理器指令,后面的两条是现代处理器新增的,而且这两条指令的目的和功能是类似的。在 IA64、x86 指令集中有 cmpxchg 指令完成 CAS 功能,在 sparc-TSO 也有 casa 指令实现,而在 ARM 和 PowerPC 架构下,则需要使用一对 ldrex/strex 指令来完成 LL/SC 的功能。 - -**CAS** 指令需要有 3 个操作数,分别是内存位置(在 Java 中可以简单理解为变量的内存地址,用 V 表示)、旧的预期值(用 A 表示)和新值(用 B 表示)。CAS 指令执行时,当且仅当 V 符合旧预期值 A 时,处理器用新值 B 更新 V 的值,否则它就不执行更新,但是无论是否更新了 V 的值,都会返回 V 的旧值,上述的处理过程是一个原子操作。 - -在 JDK 1.5 之后,Java 程序中才可以使用 CAS 操作,该操作由 sun.misc.Unsafe 类里面的 compareAndSwapInt() 和 compareAndSwapLong() 等几个方法包装提供,虚拟机在内部对这些方法做了特殊处理,即时编译出来的结果就是一条平台相关的处理器 CAS 指令,没有方法调用的过程,或者可以认为是无条件内联进去了。 - -由于 Unsafe 类不是提供给用户程序调用的类(Unsafe.getUnsafe() 的代码中限制了只有启动类加载器(Bootstrap ClassLoader)加载的 Class 才能访问它),因此,如果不采用反射手段,我们只能通过其他的 Java API 来间接使用它,如 J.U.C 包里面的整数原子类,其中的 compareAndSet() 和 getAndIncrement() 等方法都使用了 Unsafe 类的 CAS 操作。 - -这段 20 个线程自增 10000 次的代码使用了 AtomicInteger 之后程序输出了正确结果,一切都要归功于 incrementAndGet() 方法的原子性。 - -代码清单 4:Atomic 的原子自增运算 - -```java -/** - * Atomic 变量自增运算测试 - * - * @author zzm -*/ -public class AtomicTest { - - public static AtomicInteger race = new AtomicInteger(0); - - public static void increase() { - race.incrementAndGet(); - } - - private static final int THREADS_COUNT = 20; - - public static void main(String[] args) throws Exception { - Thread[] threads = new Thread[THREADS_COUNT]; - for (int i = 0; i < THREADS_COUNT; i++) { - threads[i] = new Thread(new Runnable() { - @Override - public void run() { - for (int i = 0; i < 10000; i++) { - increase(); - } - } - }); - threads[i].start(); - } - - while (Thread.activeCount() > 1) - Thread.yield(); - - System.out.println(race); - } -} -``` - -``` -200000 -``` - -incrementAndGet() 的实现其实非常简单。 - -代码清单 5:incrementAndGet() 方法的 JDK 源码 - -```java -/** - * Atomically increment by one the current value. - * @return the updated value - */ -public final int incrementAndGet() { - for (;;) { - int current = get(); - int next = current + 1; - if (compareAndSet(current, next)) - return next; - } -} -``` - -incrementAndGet() 方法在一个无限循环中,不断尝试将一个比当前值大 1 的新值赋给自己。如果失败了,那说明在执行“获取-设置”操作的时候值已经有了修改,于是再次循环进行下一次操作,直到设置成功为止。 - -尽管 CAS 看起来很美,但显然这种操作无法涵盖互斥同步的所有使用场景,并且 CAS 从语义上来说并不是完美的,存在这样的一个逻辑漏洞:如果一个变量 V 初次读取的时候是 A 值,并且在准备赋值的时候检查到它仍然为 A 值,那我们就能说它的值没有被其他线程改变过了吗?如果在这段期间它的值曾经被改成了 B,后来又被改回为 A,那 CAS 操作就会误认为它从来没有被改变过。这个漏洞称为 CAS 操作的“ABA”问题。J.U.C 包为了解决这个问题,提供了一个带有标记的原子引用类“AtomicStampedReference”,它可以通过控制变量值的版本来保证 CAS 的正确性。不过目前来说这个类比较“鸡肋”,大部分情况下 ABA 问题不会影响程序并发的正确性,如果需要解决 ABA 问题,改用传统的互斥同步可能会比原子类更高效。 - -### 2.3 无同步方案 - -要保证线程安全,并不是一定就要进行同步,两者没有因果关系。同步只是保证共享数据争用时的正确性的手段,如果一个方法本来就不涉及共享数据,那它自然就无须任何同步措施去保证正确性,因此会有一些代码天生就是线程安全的,笔者简单地介绍其中的两类。 - -**可重入代码** (Reentrant Code):这种代码也叫做纯代码(Pure Code),可以在代码执行的任何时刻中断它,转而去执行另外一段代码(包括递归调用它本身),而在控制权返回后,原来的程序不会出现任何错误。相对线程安全来说,可重入性是更基本的特性,它可以保证线程安全,即所有的可重入的代码都是线程安全的,但是并非所有的线程安全的代码都是可重入的。 +这种代码也叫做纯代码(Pure Code),可以在代码执行的任何时刻中断它,转而去执行另外一段代码(包括递归调用它本身),而在控制权返回后,原来的程序不会出现任何错误。相对线程安全来说,可重入性是更基本的特性,它可以保证线程安全,即所有的可重入的代码都是线程安全的,但是并非所有的线程安全的代码都是可重入的。 可重入代码有一些共同的特征,例如不依赖存储在堆上的数据和公用的系统资源、用到的状态量都由参数中传入、不调用非可重入的方法等。我们可以通过一个简单的原则来判断代码是否具备可重入性:如果一个方法,它的返回结果是可以预测的,只要输入了相同的数据,就都能返回相同的结果,那它就满足可重入性的要求,当然也就是线程安全的。 -**线程本地存储** (Thread Local Storage):如果一段代码中所需要的数据必须与其他代码共享,那就看看这些共享数据的代码是否能保证在同一个线程中执行?如果能保证,我们就可以把共享数据的可见范围限制在同一个线程之内,这样,无须同步也能保证线程之间不出现数据争用的问题。 +**线程本地存储(Thread Local Storage)** + +如果一段代码中所需要的数据必须与其他代码共享,那就看看这些共享数据的代码是否能保证在同一个线程中执行。如果能保证,我们就可以把共享数据的可见范围限制在同一个线程之内,这样,无须同步也能保证线程之间不出现数据争用的问题。 符合这种特点的应用并不少见,大部分使用消费队列的架构模式(如“生产者-消费者”模式)都会将产品的消费过程尽量在一个线程中消费完,其中最重要的一个应用实例就是经典 Web 交互模型中的“一个请求对应一个服务器线程”(Thread-per-Request)的处理方式,这种处理方式的广泛应用使得很多 Web 服务端应用都可以使用线程本地存储来解决线程安全问题。 Java 语言中,如果一个变量要被多线程访问,可以使用 volatile 关键字声明它为“易变的”;如果一个变量要被某个线程独享,Java 中就没有类似 C++中 \_\_declspec(thread)这样的关键字,不过还是可以通过 java.lang.ThreadLocal 类来实现线程本地存储的功能。每一个线程的 Thread 对象中都有一个 ThreadLocalMap 对象,这个对象存储了一组以 ThreadLocal.threadLocalHashCode 为键,以本地线程变量为值的 K-V 值对,ThreadLocal 对象就是当前线程的 ThreadLocalMap 的访问入口,每一个 ThreadLocal 对象都包含了一个独一无二的 threadLocalHashCode 值,使用这个值就可以在线程 K-V 值对中找回对应的本地线程变量。 -# 十、锁优化 +ThreadLocal 从理论上讲并不是用来解决多线程并发问题的,因为根本不存在多线程竞争。在一些场景 (尤其是使用线程池) 下, 由于 ThreadLocal.ThreadLocalMap 的底层数据结构导致 ThreadLocal 有内存泄漏的情况,尽可能在每次使用 ThreadLocal 后手动调用 remove(),以避免出现 ThreadLocal 经典的内存泄漏甚至是造成自身业务混乱的风险。 + +## 锁优化 高效并发是从 JDK 1.5 到 JDK 1.6 的一个重要改进,HotSpot 虚拟机开发团队在这个版本上花费了大量的精力去实现各种锁优化技术,如适应性自旋(Adaptive Spinning)、锁消除(Lock Elimination)、锁粗化(Lock Coarsening)、轻量级锁(Lightweight Locking)和偏向锁(Biased Locking)等。这些技术都是为了在线程之间更高效地共享数据,以及解决竞争问题,从而提高程序的执行效率。 -## 1. 自旋锁与自适应自旋 +### 1. 自旋锁与自适应自旋 -前面我们讨论互斥同步的时候,提到了互斥同步对性能最大的营销阻塞的实现,挂起线程和恢复线程的操作都需要转入内核态完成,这些操作给系统的并发性能带来了很大的压力。同时,虚拟机的开发团队也注意到在许多应用上,共享数据的锁定状态只会持续很短的一段时间,为了这段时间去挂起和恢复线程并不值得。如果物理机器有一个以上的处理器,能让两个或以上的线程同时并行执行,我们就可以让后面请求锁的那个线程 “稍等一下”,但不放弃处理器的执行时间,看看持有锁的线程是否很快就会释放锁。为了让线程等待,我们只需让线程执行一个忙循环(自旋),这项技术就是所谓的自旋锁。 +前面我们讨论互斥同步的时候,提到了互斥同步对性能最大的影响是阻塞的实现,挂起线程和恢复线程的操作都需要转入内核态完成,这些操作给系统的并发性能带来了很大的压力。同时,虚拟机的开发团队也注意到在许多应用上,共享数据的锁定状态只会持续很短的一段时间,为了这段时间去挂起和恢复线程并不值得。如果物理机器有一个以上的处理器,能让两个或以上的线程同时并行执行,我们就可以让后面请求锁的那个线程 “稍等一下”,但不放弃处理器的执行时间,看看持有锁的线程是否很快就会释放锁。为了让线程等待,我们只需让线程执行一个忙循环(自旋),这项技术就是所谓的自旋锁。 -自旋锁在 JDK 1.4.2 中就已经引入,只不过默认是关闭的,可以使用 -XX:+UseSpinning 参数来开启,在 JDK 1.6 就已经改为默认开启了。自旋等待不能代替阻塞,且先不说对处理器数量的要求,自旋等待本身虽然避免了线程切换的开销,但它是要占用处理器时间的,因此,如果锁被占用的时间很短,自旋等待的效果就会非常好,反之,如果锁被占用的时候很长,那么自旋的线程只会白白消耗处理器资源,而不会做任何有用的工作,反而会带来性能上的浪费。因此,自旋等待的时间必须要有一定的限度,如果自旋超过了限定的次数仍然没有成功获得锁,就应当使用传统的方式去挂起线程了。自旋次数的默认值是 10 次,用户可以使用参数 -XX:PreBlockSpin 来更改。 +自旋等待本身虽然避免了线程切换的开销,但它是要占用处理器时间的,因此,如果锁被占用的时间很短,自旋等待的效果就会非常好。反之,如果锁被占用的时候很长,那么自旋的线程只会白白消耗处理器资源,而不会做任何有用的工作,反而会带来性能上的浪费。因此,自旋等待的时间必须要有一定的限度,如果自旋超过了限定的次数仍然没有成功获得锁,就应当使用传统的方式去挂起线程了。自旋次数的默认值是 10 次,用户可以使用参数 -XX:PreBlockSpin 来更改。 -在 JDK 1.6 中引入了自适应的自旋锁。自适应意味着自旋的时间不再固定了,而是由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定。如果在同一个锁对象上,自旋等待刚刚成功获得过锁,并且持有锁的线程正在运行中,那么虚拟机就会认为这次自旋也很有可能再次成功,进而它将允许自旋等待持续相对更长的时间,比如 100 个循环。另外,如果对于某个锁,自旋很少成功获得过,那在以后要获取这个锁时将可能省略掉自旋过程,以避免浪费处理器资源。有了自适应自旋,随着程序运行和性能监控信息的不断完善,虚拟机对程序锁的状况预测就会越来越准确,虚拟机就会变得越来越 “聪明” 了。 +在 JDK 1.6 中引入了自适应的自旋锁。自适应意味着自旋的时间不再固定了,而是由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定。如果在同一个锁对象上,自旋等待刚刚成功获得过锁,并且持有锁的线程正在运行中,那么虚拟机就会认为这次自旋也很有可能再次成功,进而它将允许自旋等待持续相对更长的时间,比如 100 个循环。另外,如果对于某个锁,自旋很少成功获得过,那在以后要获取这个锁时将可能省略掉自旋过程,以避免浪费处理器资源。有了自适应自旋,随着程序运行和性能监控信息的不断完善,虚拟机对程序锁的状况预测就会越来越准确,虚拟机就会变得越来越“聪明”了。 -## 2. 锁消除 +### 2. 锁消除 锁消除是指虚拟机即时编译器在运行时,对一些代码上要求同步,但是被检测到不可能存在共享数据竞争的锁进行消除。锁消除的主要判定依据来源于逃逸分析的数据支持,如果判定在一段代码中,堆上的所有数据都不会逃逸出去从而被其他线程访问到,那就可以把他们当做栈上数据对待,认为它们是线程私有的,同步加锁自然就无须进行。 也许读者会有疑问,变量是否逃逸,对于虚拟机来说需要使用数据流分析来确定,但是程序自己应该是很清楚的,怎么会在明知道不存在数据争用的情况下要求同步呢?答案是有许多同步措施并不是程序员自己加入的。同步的代码在 Java 程序中的普遍程度也许超过了大部分读者的想象。下面段非常简单的代码仅仅是输出 3 个字符串相加的结果,无论是源码字面上还是程序语义上都没有同步。 -代码清单 6:一段看起来没有同步的代码 - ```java public static String concatString(String s1, String s2, String s3) { return s1 + s2 + s3; @@ -851,8 +720,6 @@ public static String concatString(String s1, String s2, String s3) { 我们也知道,由于 String 是一个不可变的类,对字符串的连接操作总是通过生成新的 String 对象来进行的,因此 Javac 编译器会对 String 连接做自动优化。在 JDK 1.5 之前,会转化为 StringBuffer 对象的连续 append() 操作,在 JDK 1.5 及以后的版本中,会转化为 StringBuilder 对象的连续 append() 操作,即上面的代码可能会变成下面的样子: -代码清单 7:Javac 转化后的字符串连接操作 - ```java public static String concatString(String s1, String s2, String s3) { StringBuffer sb = new StringBuffer(); @@ -862,69 +729,64 @@ public static String concatString(String s1, String s2, String s3) { return sb.toString(); } ``` -每个 StringBuffer.append() 方法中都有一个同步块,锁就是 sb 对象。虚拟机观察变量 sb,很快就会发现它的动态作用域被限制在 concatString() 方法内部。也就是说,sb 的所有引用永远不会 “逃逸” 到 concatString() 方法之外,其他线程无法访问到它,因此,虽然这里有锁,但是可以被安全地消除掉,在即时编译之后,这段代码就会忽略掉所有的同步而直接执行了。 +每个 StringBuffer.append() 方法中都有一个同步块,锁就是 sb 对象。虚拟机观察变量 sb,很快就会发现它的动态作用域被限制在 concatString() 方法内部。也就是说,sb 的所有引用永远不会 “逃逸” 到 concatString() 方法之外,其他线程无法访问到它。因此,虽然这里有锁,但是可以被安全地消除掉,在即时编译之后,这段代码就会忽略掉所有的同步而直接执行了。 -## 3. 锁粗化 +### 3. 锁粗化 -原则上,我们在编写代码的时候,总是推荐将同步块的作用范围限制得尽量小——只在共享数据的实际作用域中才进行同步,这样是为了使得需要同步的操作数量尽可能变小,如果存在锁竞争,那等待锁的线程也能尽快拿到锁。 +原则上,我们在编写代码的时候,总是推荐将同步块的作用范围限制得尽量小:只在共享数据的实际作用域中才进行同步。这样是为了使得需要同步的操作数量尽可能变小,如果存在锁竞争,那等待锁的线程也能尽快拿到锁。 大部分情况下,上面的原则都是正确的,但是如果一系列的连续操作都对同一个对象反复加锁和解锁,甚至加锁操作是出现在循环体中,那即使没有线程竞争,频繁地进行互斥同步操作也会导致不必要的性能损耗。 -代码清单 7 中连续的 append() 方法就属于这类情况。如果虚拟机探测到由这样的一串零碎的操作都对同一个对象加锁,将会把加锁同步的范围扩展(粗化)到整个操作序列的外部,以代码清单 7 为例,就是扩展到第一个 append() 操作之前直至最后一个 append() 操作之后,这样只需要加锁一次就可以了。 +上一节的示例代码中连续的 append() 方法就属于这类情况。如果虚拟机探测到由这样的一串零碎的操作都对同一个对象加锁,将会把加锁同步的范围扩展(粗化)到整个操作序列的外部。对于上一节的示例代码就是扩展到第一个 append() 操作之前直至最后一个 append() 操作之后,这样只需要加锁一次就可以了。 -## 4. 轻量级锁 +### 4. 轻量级锁 -轻量级锁是 JDK 1.6 之中加入的新型锁机制,它名字中的 “轻量级” 是相对于使用操作系统互斥量来实现的传统锁而言的,因此传统的锁机制就称为 “重量级” 锁。首先需要强调一点的是,轻量级锁并不是用来代替重要级锁的,它的本意是在没有多线程竞争的前提下,减少传统的重量级锁使用操作系统互斥量产生的性能消耗。 +轻量级锁是 JDK 1.6 之中加入的新型锁机制,它名字中的“轻量级”是相对于使用操作系统互斥量来实现的传统锁而言的,因此传统的锁机制就称为“重量级”锁。首先需要强调一点的是,轻量级锁并不是用来代替重要级锁的,它的本意是在没有多线程竞争的前提下,减少传统的重量级锁使用操作系统互斥量产生的性能消耗。 -要理解轻量级锁,以及后面会讲到的偏向锁的原理和运作过程,必须从 HotSpot 虚拟机的对象(对象头部分)的内存布局开始介绍。HotSpot 虚拟机的对象头(Object Header)分为两部分信息,第一部分用于存储对象自身的运行时数据,如哈希码(HashCode)、GC 分代年龄(Generational GC Age)等,这部分数据是长度在 32 位和 64 位的虚拟机中分别为 32 bit 和 64 bit,官方称它为 “Mark Word”,它是实现轻量级锁和偏向锁的关键。另外一部分用于存储指向方法区对象类型数据的指针,如果是数组对象的话,还会有一个额外的部分用于存储数组长度。 +要理解轻量级锁,以及后面会讲到的偏向锁的原理和运作过程,必须从 HotSpot 虚拟机的对象(对象头部分)的内存布局开始介绍。HotSpot 虚拟机的对象头(Object Header)分为两部分信息,第一部分用于存储对象自身的运行时数据,如哈希码(HashCode)、GC 分代年龄(Generational GC Age)等,这部分数据是长度在 32 位和 64 位的虚拟机中分别为 32 bit 和 64 bit,官方称它为“Mark Word”,它是实现轻量级锁和偏向锁的关键。另外一部分用于存储指向方法区对象类型数据的指针,如果是数组对象的话,还会有一个额外的部分用于存储数组长度。 -对象头信息是与对象自身定义的数据无关的额外存储成本,考虑到虚拟机的空间效率,Mark Work 被设计成一个非固定的数据结构以便在极小的空间内存储尽量多的信息,它会根据对象的状态复用自己的存储空间。例如,在 32 位的 HotSpot 虚拟机中对象未被锁定的状态下,Mark Word 的 32bit 空间中的 25bit 用于存储对象哈希码(HashCode),4bit 用于存储对象分代年龄,2bit 用于存储锁标志位,1bit 固定为 0,在其他状态(轻量级锁定、重量级锁定、GC 标记、可偏向)下对象的存储内容见表 13-1。 +简单地介绍了对象的内存布局后,我们把话题返回到轻量级锁的执行过程上。在代码进入同步块的时候,如果此同步对象没有被锁定(锁标志位为 “01” 状态)虚拟机首先将在当前线程的栈帧中建立一个名为锁记录(Lock Record)的空间,用于存储锁对象目前的 Mark Word 的拷贝(官方把这份拷贝加上了一个 Displaced 前缀,即 Displaced Mark Word)。然后,虚拟机将使用 CAS 操作尝试将对象的 Mark Word 更新为指向 Lock Record 的指针。如果这个更新动作成功了,那么这个线程就拥有了该对象的锁,并且对象 Mark Word 的锁标志位(Mark Word 的最后 2bit)将转变为 “00”,即表示此对象处于轻量级锁定状态。 -

+

-简单地介绍了对象的内存布局后,我们把话题返回到轻量级锁的执行过程上。在代码进入同步块的时候,如果此同步对象没有被锁定(锁标志位为 “01” 状态)虚拟机首先将在当前线程的栈帧中建立一个名为锁记录(Lock Record)的空间,用于存储锁对象目前的 Mark Word 的拷贝(官方把这份拷贝加上了一个 Displaced 前缀,即 Displaced Mark Word),这时候线程堆栈与对象头的状态如图 13-3 所示。 - -

- -然后,虚拟机将使用 CAS 操作尝试将对象的 Mark Word 更新为指向 Lock Record 的指针。如果这个更新动作成功了,那么这个线程就拥有了该对象的锁,并且对象 Mark Word 的锁标志位 (Mark Word 的最后 2bit)将转变为 “00”,即表示此对象处于轻量级锁定状态,这时候线程堆栈与对象头的状态如图 12-4 所示。 - -

- -如果这个更新操作失败了,虚拟机首先会检查对象的 Mark Word 是否指向当前线程的栈帧,如果只说明当前线程已经拥有了这个对象的锁,那就可以直接进入同步块继续执行,否则说明这个锁对象以及被其他线程线程抢占了。如果有两条以上的线程争用同一个锁,那轻量级锁就不再有效,要膨胀为重量级锁,所标志的状态变为 “10”,Mark Word 中存储的就是指向重量级锁(互斥量)的指针,后面等待锁的线程也要进入阻塞状态。 +如果这个更新操作失败了,虚拟机首先会检查对象的 Mark Word 是否指向当前线程的栈帧,如果是的话只说明当前线程已经拥有了这个对象的锁,那就可以直接进入同步块继续执行,否则说明这个锁对象已经被其他线程线程抢占了。如果有两条以上的线程争用同一个锁,那轻量级锁就不再有效,要膨胀为重量级锁,所标志的状态变为“10”,Mark Word 中存储的就是指向重量级锁(互斥量)的指针,后面等待锁的线程也要进入阻塞状态。 上面描述的是轻量级锁的加锁过程,它的解锁过程也是通过 CAS 操作来进行的,如果对象的 Mark Word 仍然指向着线程的锁记录,那就用 CAS 操作把对象当前的 Mark Word 和线程中复制的 Displaced Mark Word 替换回来,如果替换成功,整个同步过程就完成了。如果替换失败,说明有其他线程尝试过获取该锁,那就要释放锁的同时,唤醒被挂起的线程。 -轻量级锁能提升程序同步性能的依据是 “对于绝大部分的锁,在整个同步周期内都是不存在竞争的”,这是一个经验数据。如果没有竞争,轻量级锁使用 CAS 操作避免了使用互斥量的开销,但如果存在锁竞争,除了互斥量的开销外,还额外发生了 CAS 操作,因此在有竞争的情况下,轻量级锁会比传统的重量级锁更慢。 +轻量级锁能提升程序同步性能的依据是“对于绝大部分的锁,在整个同步周期内都是不存在竞争的”,这是一个经验数据。如果没有竞争,轻量级锁使用 CAS 操作避免了使用互斥量的开销,但如果存在锁竞争,除了互斥量的开销外,还额外发生了 CAS 操作,因此在有竞争的情况下,轻量级锁会比传统的重量级锁更慢。 -## 5. 偏向锁 +### 5. 偏向锁 偏向锁也是 JDK 1.6 中引入的一项锁优化,它的目的是消除数据在无竞争情况下的同步原语,进一步提高程序的运行性能。如果说轻量级锁是在无竞争的情况下使用 CAS 操作去消除同步使用的互斥量,那偏向锁就是在无竞争的情况下把整个同步都消除掉,连 CAS 操作都不做了。 -偏向锁的 “偏”,就是偏心的 “偏”、偏袒的 “偏”,它的意思是这个锁会偏向于第一个获得它的线程,如果在接下来的执行过程中,该锁没有被其他的线程获取,则持有偏向锁的线程将永远不需要再进行同步。 +偏向锁的“偏”,就是偏心的“偏”、偏袒的“偏”,它的意思是这个锁会偏向于第一个获得它的线程,如果在接下来的执行过程中,该锁没有被其他的线程获取,则持有偏向锁的线程将永远不需要再进行同步。 -如果读者读懂了前面轻量级锁中关于对象头 Mark Word 与线程之间的操作过程,那偏向锁的原理理解起来就会很简单。假设当前虚拟机启用了偏向锁(启用参数 -XX:+UseBiasedLocking,这是 JDK 1.6 的默认值),那么,当锁对象第一次被线程获取的时候,虚拟机将会把对象头中的标志位设为 “01”,即偏向模式。同时使用 CAS 操作把获取到这个锁的线程 ID 记录在对象的 Mark Word 之中,如果 CAS 操作成功,持有偏向锁的线程以后每次进入这个锁相关的同步块时,虚拟机都可以不再进行如何同步操作(例如 Locking、Unlocking 及对 Mark Word 的 Update 等)。 +假设当前虚拟机启用了偏向锁(启用参数 -XX:+UseBiasedLocking,这是 JDK 1.6 的默认值),那么,当锁对象第一次被线程获取的时候,虚拟机将会把对象头中的标志位设为“01”,即偏向模式。同时使用 CAS 操作把获取到这个锁的线程 ID 记录在对象的 Mark Word 之中,如果 CAS 操作成功,持有偏向锁的线程以后每次进入这个锁相关的同步块时,虚拟机都可以不再进行如何同步操作(例如 Locking、Unlocking 及对 Mark Word 的 Update 等)。 -当有另外一个线程去尝试获取这个锁时,偏向模式就宣告结束。根据锁对象目前是否处于被锁定的状态,撤销偏向(Revoke Bias)后恢复到未锁定(标志位为 “01”)或轻量级锁定(标志位为 “00”)的状态,后续的同步操作就如上面介绍的轻量级锁那样执行。偏向锁、轻量级锁的状态转换及对象 Mark Word 的关系如图 13-5 所示。 +当有另外一个线程去尝试获取这个锁时,偏向模式就宣告结束。根据锁对象目前是否处于被锁定的状态,撤销偏向(Revoke Bias)后恢复到未锁定(标志位为“01”)或轻量级锁定(标志位为“00”)的状态,后续的同步操作就如上面介绍的轻量级锁那样执行。偏向锁、轻量级锁的状态转换及对象 Mark Word 的关系如图 13-5 所示。 -

+

偏向锁可以提高带有同步但无竞争的程序性能。它同样是一个带有效益权衡(Trade Off)性质的优化,也就是说,它并不一定总是对程序运行有利,如果程序中大多数的锁总是被多个不同的线程访问,那偏向模式就是多余的。在具体问题具体分析的前提下,有时候使用参数 -XX:-UseBiasedLocking 来禁止偏向锁优化反而可以提升性能。 -# 十一、多线程开发良好的实践 +# 九、多线程开发良好的实践 -- 给线程命名。 -- 最小化同步范围。 -- 优先使用 volatile。 -- 尽可能使用更高层次的并发工具而非 wait 和 notify() 来实现线程通信,如 BlockingQueue, Semeaphore。 -- 多用并发容器,少用同步容器,并发容器比同步容器的可扩展性更好。 -- 考虑使用线程池。 -- 最低限度的使用同步和锁,缩小临界区。因此相对于同步方法,同步块会更好。 +1. 给线程起个有意义的名字,这样可以方便找 Bug; + +2. 因为锁花费的代价很高,应该尽可能减小同步范围; + +3. 多用同步类少用 wait 和 notify。首先,CountDownLatch, Semaphore, CyclicBarrier 和 Exchanger 这些同步类简化了编码操作,而用 wait 和 notify 很难实现对复杂控制流的控制。其次,这些类是由最好的企业编写和维护在后续的 JDK 中它们还会不断优化和完善,使用这些更高等级的同步工具你的程序可以不费吹灰之力获得优化。 + +4. 多用并发集合少用同步集合。并发集合比同步集合的可扩展性更好,例如应该使用 ConcurrentHashMap 而不是 Hashttable。 # 参考资料 -- Java 编程思想 -- 深入理解 Java 虚拟机 +- BruceEckel. Java 编程思想: 第 4 版 [M]. 机械工业出版社, 2007. +- 周志明. 深入理解 Java 虚拟机 [M]. 机械工业出版社, 2011. - [线程通信](http://ifeve.com/thread-signaling/#missed_signal) - [Java 线程面试题 Top 50](http://www.importnew.com/12773.html) - [BlockingQueue](http://tutorials.jenkov.com/java-util-concurrent/blockingqueue.html) - [thread state java](https://stackoverflow.com/questions/11265289/thread-state-java) +- [CSC 456 Spring 2012/ch7 MN](http://wiki.expertiza.ncsu.edu/index.php/CSC_456_Spring_2012/ch7_MN) +- [Java - Understanding Happens-before relationship](https://www.logicbig.com/tutorials/core-java-tutorial/java-multi-threading/happens-before.html) +- [6장 Thread Synchronization](https://www.slideshare.net/novathinker/6-thread-synchronization) diff --git a/notes/Java 虚拟机.md b/notes/Java 虚拟机.md index 52ab99b6..81c5dff0 100644 --- a/notes/Java 虚拟机.md +++ b/notes/Java 虚拟机.md @@ -27,9 +27,7 @@ # 一、运行时数据区域 -

- -注:白色区域为线程私有,蓝色区域为线程共享。 +

## 程序计数器 @@ -39,6 +37,8 @@ 每个 Java 方法在执行的同时会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行完成的过程,就对应着一个栈帧在 Java 虚拟机栈中入栈和出栈的过程。 +

+ 可以通过 -Xss 这个虚拟机参数来指定一个程序的 Java 虚拟机栈内存大小: ```java @@ -56,6 +56,8 @@ java -Xss=512M HackTheJava 与 Java 虚拟机栈类似,它们之间的区别只不过是本地方法栈为本地方法服务。 +

+ ## Java 堆 所有对象实例都在这里分配内存。 @@ -72,14 +74,14 @@ java -Xss=512M HackTheJava - From Survivor - To Survivor -

+

-Java 堆不需要连续内存,并且可以通过动态增加其内存,增加失败会抛出 OutOfMemoryError 异常。 +Java 堆不需要连续内存,并且可以动态增加其内存,增加失败会抛出 OutOfMemoryError 异常。 -可以通过 -Xms 和 -Xmx 两个虚拟机参数来指定一个程序的 Java 堆内存大小,第一个参数设置最小值,第二个参数设置最大值。 +可以通过 -Xms 和 -Xmx 两个虚拟机参数来指定一个程序的 Java 堆内存大小,第一个参数设置初始值,第二个参数设置最大值。 ```java -java -Xms=1M -XmX=2M HackTheJava +java -Xms=1M -Xmx=2M HackTheJava ``` ## 方法区 @@ -123,6 +125,8 @@ objB.instance = objA; 通过 GC Roots 作为起始点进行搜索,能够到达到的对象都是都是可用的,不可达的对象可被回收。 +

+ GC Roots 一般包含以下内容: 1. 虚拟机栈中引用的对象 @@ -132,7 +136,7 @@ GC Roots 一般包含以下内容: ### 3. 引用类型 -无论是通过引用计算算法判断对象的引用数量,还是通过可达性分析算法判断对象的引用链是否可达,判定对象是否存活都与“引用”有关。 +无论是通过引用计算算法判断对象的引用数量,还是通过可达性分析算法判断对象的引用链是否可达,判定对象是否存活都与引用有关。 Java 对引用的概念进行了扩充,引入四种强度不同的引用类型。 @@ -150,7 +154,7 @@ Object obj = new Object(); 用来描述一些还有用但是并非必需的对象。 -在系统将要发生内存溢出异常之前,将会对这些对象列进回收范围之中进行第二次回收。如果这次回收还没有足够的内存,才会抛出溢出异常。 +在系统将要发生内存溢出异常之前,将会对这些对象列进回收范围之中进行第二次回收。 软引用主要用来实现类似缓存的功能,在内存足够的情况下直接通过软引用取值,无需从繁忙的真实来源获取数据,提升速度;当内存不足时,自动删除这部分缓存数据,从真正的来源获取这些数据。 @@ -211,20 +215,20 @@ finalize() 类似 C++ 的析构函数,用来做关闭外部资源等工作。 ### 1. 标记 - 清除 -

+

将需要回收的对象进行标记,然后清除。 不足: 1. 标记和清除过程效率都不高 -2. 会产生大量碎片,内存碎片过多可能导致无法给大对象分配内存 +2. 会产生大量碎片,内存碎片过多可能导致无法给大对象分配内存。 之后的算法都是基于该算法进行改进。 ### 2. 复制 -

+

将内存划分为大小相等的两块,每次只使用其中一块,当这一块内存用完了就将还存活的对象复制到另一块上面,然后再把使用过的内存空间进行一次清理。 @@ -234,7 +238,7 @@ finalize() 类似 C++ 的析构函数,用来做关闭外部资源等工作。 ### 3. 标记 - 整理 -

+

让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。 @@ -249,13 +253,13 @@ finalize() 类似 C++ 的析构函数,用来做关闭外部资源等工作。 ## 垃圾收集器 -

+

以上是 HotSpot 虚拟机中的 7 个垃圾收集器,连线表示垃圾收集器可以配合使用。 ### 1. Serial 收集器 -

+

它是单线程的收集器,不仅意味着只会使用一个线程进行垃圾收集工作,更重要的是它在进行垃圾收集时,必须暂停所有其他工作线程,往往造成过长的等待时间。 @@ -265,7 +269,7 @@ finalize() 类似 C++ 的析构函数,用来做关闭外部资源等工作。 ### 2. ParNew 收集器 -

+

它是 Serial 收集器的多线程版本。 @@ -287,7 +291,7 @@ finalize() 类似 C++ 的析构函数,用来做关闭外部资源等工作。 ### 4. Serial Old 收集器 -

+

Serial Old 是 Serial 收集器的老年代版本,也是给 Client 模式下的虚拟机使用。如果用在 Server 模式下,它有两大用途: @@ -296,7 +300,7 @@ Serial Old 是 Serial 收集器的老年代版本,也是给 Client 模式下 ### 5. Parallel Old 收集器 -

+

是 Parallel Scavenge 收集器的老年代版本。 @@ -304,7 +308,7 @@ Serial Old 是 Serial 收集器的老年代版本,也是给 Client 模式下 ### 6. CMS 收集器 -

+

CMS(Concurrent Mark Sweep),从 Mark Sweep 可以知道它是基于标记 - 清除算法实现的。 @@ -329,7 +333,7 @@ CMS(Concurrent Mark Sweep),从 Mark Sweep 可以知道它是基于标记 - ### 7. G1 收集器 -

+

G1(Garbage-First)收集器是当今收集器技术发展最前沿的成果之一,它是一款面向服务端应用的垃圾收集器,HotSpot 开发团队赋予它的使命是(在比较长期的)未来可以替换掉 JDK 1.5 中发布的 CMS 收集器。 @@ -426,7 +430,7 @@ JVM 并不是永远地要求对象的年龄必须达到 MaxTenuringThreshold 才 ## 类的生命周期 -

+

包括以下 7 个阶段: @@ -616,7 +620,7 @@ public static void main(String[] args) { 应用程序都是由三种类加载器相互配合进行加载的,如果有必要,还可以加入自己定义的类加载器。下图展示的类加载器之间的层次关系,称为类加载器的双亲委派模型(Parents Delegation Model)。该模型要求除了顶层的启动类加载器外,其余的类加载器都应有自己的父类加载器,这里类加载器之间的父子关系一般通过组合(Composition)关系来实现,而不是通过继承(Inheritance)的关系实现。 -

+

**(一)工作过程** @@ -681,6 +685,11 @@ java -Xmx12m -Xms3m -Xmn1m -XX:PermSize=20m -XX:MaxPermSize=20m -XX:+UseSerialGC # 参考资料 -- 深入理解 Java 虚拟机 +- 周志明. 深入理解 Java 虚拟机 [M]. 机械工业出版社, 2011. - [Jvm memory](https://www.slideshare.net/benewu/jvm-memory) - [Memory Architecture Of JVM(Runtime Data Areas)](https://hackthejava.wordpress.com/2015/01/09/memory-architecture-by-jvmruntime-data-areas/) +- [JVM Run-Time Data Areas](https://www.programcreek.com/2013/04/jvm-run-time-data-areas/) +- [Android on x86: Java Native Interface and the Android Native Development Kit](http://www.drdobbs.com/architecture-and-design/android-on-x86-java-native-interface-and/240166271) +- [深入理解 JVM(2)——GC 算法与内存分配策略](https://crowhawk.github.io/2017/08/10/jvm_2/) +- [深入理解 JVM(3)——7 种垃圾收集器](https://crowhawk.github.io/2017/08/15/jvm_3/) +- [JVM Internals](http://blog.jamesdbloom.com/JVMInternals.html) diff --git a/notes/Leetcode 题解.md b/notes/Leetcode 题解.md index cf67cdca..6be5727b 100644 --- a/notes/Leetcode 题解.md +++ b/notes/Leetcode 题解.md @@ -13,14 +13,14 @@ * [Backtracking](#backtracking) * [分治](#分治) * [动态规划](#动态规划) - * [分割整数](#分割整数) - * [矩阵路径](#矩阵路径) * [斐波那契数列](#斐波那契数列) * [最长递增子序列](#最长递增子序列) * [最长公共子系列](#最长公共子系列) * [0-1 背包](#0-1-背包) * [数组区间](#数组区间) * [字符串编辑](#字符串编辑) + * [分割整数](#分割整数) + * [矩阵路径](#矩阵路径) * [其它问题](#其它问题) * [数学](#数学) * [素数](#素数) @@ -188,7 +188,7 @@ Input: [1,2], [1,2,3] Output: 2 Explanation: You have 2 children and 3 cookies. The greed factors of 2 children are 1, 2. -You have 3 cookies and their sizes are big enough to gratify all of the children, +You have 3 cookies and their sizes are big enough to gratify all of the children, You need to output 2. ``` @@ -420,7 +420,7 @@ public int[][] reconstructQueue(int[][] people) { 双指针主要用于遍历数组,两个指针指向不同的元素,从而协同完成任务。 -**从一个已经排序的数组中查找出两个数,使它们的和为 0** +**从一个已经排序的数组中找出两个数,使它们的和为 0** [Leetcode :167. Two Sum II - Input array is sorted (Easy)](https://leetcode.com/problems/two-sum-ii-input-array-is-sorted/description/) @@ -518,7 +518,7 @@ Output: True Explanation: You could delete the character 'c'. ``` -题目描述:字符串可以删除一个字符,判断是否能构成回文字符串。 +题目描述:可以删除一个字符,判断是否能构成回文字符串。 ```java public boolean validPalindrome(String s) { @@ -548,7 +548,7 @@ private boolean isPalindrome(String s, int l, int r){ [Leetcode : 88. Merge Sorted Array (Easy)](https://leetcode.com/problems/merge-sorted-array/description/) -题目描述:把归并结果存到第一个数组上 +题目描述:把归并结果存到第一个数组上。 ```java public void merge(int[] nums1, int m, int[] nums2, int n) { @@ -596,7 +596,7 @@ Output: "apple" ``` -题目描述:可以删除 s 中的一些字符,使得它成为字符串列表 d 中的一个字符串。要求在 d 中找到满足条件的最长字符串。 +题目描述:删除 s 中的一些字符,使得它构成字符串列表 d 中的一个字符串,找出能构成的最长字符串。如果有多个相同长度的结果,返回按字典序排序的最大字符串。 ```java public String findLongestWord(String s, List d) { @@ -632,7 +632,7 @@ public String findLongestWord(String s, List d) { [Leetocde : 215. Kth Largest Element in an Array (Medium)](https://leetcode.com/problems/kth-largest-element-in-an-array/description/) -**排序** :时间复杂度 O(nlgn),空间复杂度 O(1) 解法 +**排序** :时间复杂度 O(nlgn),空间复杂度 O(1) ```java public int findKthLargest(int[] nums, int k) { @@ -706,7 +706,7 @@ public int findKthLargest(int[] nums, int k) { ### 桶排序 -**找出出现频率最多的 k 个数** +**出现频率最多的 k 个数** [Leetcode : 347. Top K Frequent Elements (Medium)](https://leetcode.com/problems/top-k-frequent-elements/description/) @@ -714,6 +714,8 @@ public int findKthLargest(int[] nums, int k) { Given [1,1,1,2,2,3] and k = 2, return [1,2]. ``` +设置若干个桶,每个桶存储出现频率相同的数,并且桶的下标代表桶中数出现的频率,即第 i 个桶中存储的数出现的频率为 i。把数都放到桶之后,从后向前遍历桶,最先得到的 k 个数就是出现频率最多的的 k 个数。 + ```java public List topKFrequent(int[] nums, int k) { List ret = new ArrayList<>(); @@ -747,28 +749,42 @@ public List topKFrequent(int[] nums, int k) {

-广度优先搜索的搜索过程有点像一层一层地进行遍历:从节点 0 出发,遍历到 6、2、1 和 5 这四个新节点。 +广度优先搜索的搜索过程有点像一层一层地进行遍历,每层遍历都以上一层遍历的结果作为起点,遍历一个长度。需要注意的是,遍历过的节点不能再次被遍历。 -继续从 6 开始遍历,得到节点 4 ;从 2 开始遍历,没有下一个节点;从 1 开始遍历,没有下一个节点;从 5 开始遍历,得到 3 和 4 节点。这一轮总共得到两个新节点:4 和 3 。 +第一层: -反复从新节点出发进行上述的遍历操作。 +- 0 -> {6,2,1,5}; -可以看到,每一轮遍历的节点都与根节点路径长度相同。设 di 表示第 i 个节点与根节点的路径长度,推导出一个结论:对于先遍历的节点 i 与后遍历的节点 j,有 di<=dj。利用这个结论,可以求解最短路径 **最优解** 问题:第一次遍历到目的节点,其所经过的路径为最短路径,如果继续遍历,之后再遍历到目的节点,所经过的路径就不是最短路径。 +第二层: + +- 6 -> {4} +- 2 -> {} +- 1 -> {} +- 5 -> {4,3} + +第三层: + +- 4 -> {} +- 3 -> {} + +可以看到,每一轮遍历的节点都与根节点路径长度相同。设 di 表示第 i 个节点与根节点的路径长度,推导出一个结论:对于先遍历的节点 i 与后遍历的节点 j,有 di<=dj。利用这个结论,可以求解最短路径等 **最优解** 问题:第一次遍历到目的节点,其所经过的路径为最短路径,如果继续遍历,之后再遍历到目的节点,所经过的路径就不是最短路径。 在程序实现 BFS 时需要考虑以下问题: -- 队列:用来存储每一轮遍历的节点 -- 标记:对于遍历过得节点,应该将它标记,防止重复遍历; +- 队列:用来存储每一轮遍历的节点; +- 标记:对于遍历过的节点,应该将它标记,防止重复遍历。 **计算在网格中从原点到特定点的最短路径长度** ```html [[1,1,0,1], -[1,0,1,0], -[1,1,1,1], -[1,0,1,1]] + [1,0,1,0], + [1,1,1,1], + [1,0,1,1]] ``` +1 表示可以经过某个位置。 + ```java public int minPathLength(int[][] grids, int tr, int tc) { int[][] next = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}}; @@ -803,14 +819,14 @@ private class Position {

-广度优先搜索一层一层遍历,每一层遍历到的所有新节点,要用队列先存储起来以备下一层遍历的时候再遍历;而深度优先搜索在遍历到一个新节点时立马对新节点进行遍历:从节点 0 出发开始遍历,得到到新节点 6 时,立马对新节点 6 进行遍历,得到新节点 4;如此反复以这种方式遍历新节点,直到没有新节点了,此时返回。返回到根节点 0 的情况是,继续对根节点 0 进行遍历,得到新节点 2,然后继续以上步骤。 +广度优先搜索一层一层遍历,每一层得到到的所有新节点,要用队列先存储起来以备下一层遍历的时候再遍历;而深度优先搜索在得到到一个新节点时立马对新节点进行遍历:从节点 0 出发开始遍历,得到到新节点 6 时,立马对新节点 6 进行遍历,得到新节点 4;如此反复以这种方式遍历新节点,直到没有新节点了,此时返回。返回到根节点 0 的情况是,继续对根节点 0 进行遍历,得到新节点 2,然后继续以上步骤。 从一个节点出发,使用 DFS 对一个图进行遍历时,能够遍历到的节点都是从初始节点可达的,DFS 常用来求解这种 **可达性** 问题。 在程序实现 DFS 时需要考虑以下问题: -- 栈:用栈来保存当前节点信息,当遍历新节点返回时能够继续遍历当前节点。也可以使用递归栈。 -- 标记:和 BFS 一样同样需要对已经遍历过得节点进行标记。 +- 栈:用栈来保存当前节点信息,当遍历新节点返回时能够继续遍历当前节点。可以使用递归栈。 +- 标记:和 BFS 一样同样需要对已经遍历过的节点进行标记。 **查找最大的连通面积** @@ -818,13 +834,13 @@ private class Position { ```html [[0,0,1,0,0,0,0,1,0,0,0,0,0], -[0,0,0,0,0,0,0,1,1,1,0,0,0], -[0,1,1,0,1,0,0,0,0,0,0,0,0], -[0,1,0,0,1,1,0,0,1,0,1,0,0], -[0,1,0,0,1,1,0,0,1,1,1,0,0], -[0,0,0,0,0,0,0,0,0,0,1,0,0], -[0,0,0,0,0,0,0,1,1,1,0,0,0], -[0,0,0,0,0,0,0,1,1,0,0,0,0]] + [0,0,0,0,0,0,0,1,1,1,0,0,0], + [0,1,1,0,1,0,0,0,0,0,0,0,0], + [0,1,0,0,1,1,0,0,1,0,1,0,0], + [0,1,0,0,1,1,0,0,1,1,1,0,0], + [0,0,0,0,0,0,0,0,0,0,1,0,0], + [0,0,0,0,0,0,0,1,1,1,0,0,0], + [0,0,0,0,0,0,0,1,1,0,0,0,0]] ``` ```java @@ -1083,74 +1099,11 @@ private void dfs(int r, int c, boolean[][] canReach) { } ``` -**N 皇后** - -[Leetcode : 51. N-Queens (Hard)](https://leetcode.com/problems/n-queens/description/) - -

- -题目描述:在 n\*n 的矩阵中摆放 n 个皇后,并且每个皇后不能在同一行,同一列,同一对角线上,要求解所有的 n 皇后解。 - -一行一行地摆放,在确定一行中的那个皇后应该摆在哪一列时,需要用三个标记数组来确定某一列是否合法,这三个标记数组分别为:列标记数组、45 度对角线标记数组和 135 度对角线标记数组。 - -45 度对角线标记数组的维度为 2\*n - 1,通过下图可以明确 (r,c) 的位置所在的数组下标为 r + c。 - -

- -135 度对角线标记数组的维度也是 2\*n - 1,(r,c) 的位置所在的数组下标为 n - 1 - (r - c)。 - -

- -```java -private List> ret; -private char[][] nQueens; -private boolean[] colUsed; -private boolean[] diagonals45Used; -private boolean[] diagonals135Used; -private int n; - -public List> solveNQueens(int n) { - ret = new ArrayList<>(); - nQueens = new char[n][n]; - Arrays.fill(nQueens, '.'); - colUsed = new boolean[n]; - diagonals45Used = new boolean[2 * n - 1]; - diagonals135Used = new boolean[2 * n - 1]; - this.n = n; - backstracking(0); - return ret; -} - -private void backstracking(int row) { - if (row == n) { - List list = new ArrayList<>(); - for (char[] chars : nQueens) { - list.add(new String(chars)); - } - ret.add(list); - return; - } - - for (int col = 0; col < n; col++) { - int diagonals45Idx = row + col; - int diagonals135Idx = n - 1 - (row - col); - if (colUsed[col] || diagonals45Used[diagonals45Idx] || diagonals135Used[diagonals135Idx]) { - continue; - } - nQueens[row][col] = 'Q'; - colUsed[col] = diagonals45Used[diagonals45Idx] = diagonals135Used[diagonals135Idx] = true; - backstracking(row + 1); - colUsed[col] = diagonals45Used[diagonals45Idx] = diagonals135Used[diagonals135Idx] = false; - nQueens[row][col] = '.'; - } -} -``` - ### Backtracking 回溯是 DFS 的一种,它不是用在遍历图的节点上,而是用于求解 **排列组合** 问题,例如有 { 'a','b','c' } 三个字符,求解所有由这三个字符排列得到的字符串。 -在程序实现时,回溯需要注意对元素进行标记的问题。使用递归实现的回溯,在访问一个新元素进入新的递归调用,此时需要将新元素标记为已经访问,这样才能在继续递归调用时不用重复访问该元素;但是在递归返回时,需要将该元素标记为未访问,因为只需要保证在一个递归链中不同时访问一个元素,而在不同的递归链是可以访问已经访问过但是不在当前递归链中的元素。 +在程序实现时,回溯需要注意对元素进行标记的问题。使用递归实现的回溯,在访问一个新元素进入新的递归调用时,需要将新元素标记为已经访问,这样才能在继续递归调用时不用重复访问该元素;但是在递归返回时,需要将该元素标记为未访问,因为只需要保证在一个递归链中不同时访问一个元素,可以访问已经访问过但是不在当前递归链中的元素。 **数字键盘组合** @@ -1327,7 +1280,7 @@ private void backtracking(List permuteList, boolean[] visited, int[] nu [[1,1,2], [1,2,1], [2,1,1]] ``` -题目描述:数组元素可能含有相同的元素,进行排列时就有可能出先重复的排列,要求重复的排列只返回一个。 +题目描述:数组元素可能含有相同的元素,进行排列时就有可能出现 重复的排列,要求重复的排列只返回一个。 在实现上,和 Permutations 不同的是要先排序,然后在添加一个元素时,判断这个元素是否等于前一个元素,如果等于,并且前一个元素还未访问,那么就跳过这个元素。 @@ -1389,8 +1342,7 @@ private void backtracking(int start, int n, int k, List combineList, Li return; } - for(int i = start; i <= n - k + 1; i++){ // 剪枝 - + for(int i = start; i <= n - k + 1; i++) { // 剪枝 combineList.add(i); // 把 i 标记为已访问 backtracking(i + 1, n, k - 1, combineList, ret); combineList.remove(combineList.size() - 1); // 把 i 标记为未访问 @@ -1502,7 +1454,7 @@ private void backtracking(int startIdx, int size, int[] nums) { for (int i = startIdx; i < nums.length; i++) { subsetList.add(nums[i]); - backtracking(i + 1, size, nums); // startIdx 设为下一个元素,使 subset 中的元素都递增排序 + backtracking(i + 1, size, nums); subsetList.remove(subsetList.size() - 1); } } @@ -1559,10 +1511,20 @@ private void backtracking(int startIdx, int size, int[] nums) { } ``` -**分割字符串使得每部分都是回文数** +**分割字符串使得每个部分都是回文数** [Leetcode : 131. Palindrome Partitioning (Medium)](https://leetcode.com/problems/palindrome-partitioning/description/) +```html +For example, given s = "aab", +Return + +[ + ["aa","b"], + ["a","a","b"] +] +``` + ```java private List> ret; @@ -1650,6 +1612,69 @@ private int cubeNum(int i, int j) { } ``` +**N 皇后** + +[Leetcode : 51. N-Queens (Hard)](https://leetcode.com/problems/n-queens/description/) + +

+ +题目描述:在 n\*n 的矩阵中摆放 n 个皇后,并且每个皇后不能在同一行,同一列,同一对角线上,要求解所有的 n 皇后解。 + +一行一行地摆放,在确定一行中的那个皇后应该摆在哪一列时,需要用三个标记数组来确定某一列是否合法,这三个标记数组分别为:列标记数组、45 度对角线标记数组和 135 度对角线标记数组。 + +45 度对角线标记数组的维度为 2\*n - 1,通过下图可以明确 (r,c) 的位置所在的数组下标为 r + c。 + +

+ +135 度对角线标记数组的维度也是 2\*n - 1,(r,c) 的位置所在的数组下标为 n - 1 - (r - c)。 + +

+ +```java +private List> ret; +private char[][] nQueens; +private boolean[] colUsed; +private boolean[] diagonals45Used; +private boolean[] diagonals135Used; +private int n; + +public List> solveNQueens(int n) { + ret = new ArrayList<>(); + nQueens = new char[n][n]; + Arrays.fill(nQueens, '.'); + colUsed = new boolean[n]; + diagonals45Used = new boolean[2 * n - 1]; + diagonals135Used = new boolean[2 * n - 1]; + this.n = n; + backstracking(0); + return ret; +} + +private void backstracking(int row) { + if (row == n) { + List list = new ArrayList<>(); + for (char[] chars : nQueens) { + list.add(new String(chars)); + } + ret.add(list); + return; + } + + for (int col = 0; col < n; col++) { + int diagonals45Idx = row + col; + int diagonals135Idx = n - 1 - (row - col); + if (colUsed[col] || diagonals45Used[diagonals45Idx] || diagonals135Used[diagonals135Idx]) { + continue; + } + nQueens[row][col] = 'Q'; + colUsed[col] = diagonals45Used[diagonals45Idx] = diagonals135Used[diagonals135Idx] = true; + backstracking(row + 1); + colUsed[col] = diagonals45Used[diagonals45Idx] = diagonals135Used[diagonals135Idx] = false; + nQueens[row][col] = '.'; + } +} +``` + ## 分治 **给表达式加括号** @@ -1694,122 +1719,6 @@ public List diffWaysToCompute(String input) { 递归和动态规划都是将原问题拆成多个子问题然后求解,他们之间最本质的区别是,动态规划保存了子问题的解。 -### 分割整数 - -**分割整数的最大乘积** - -[Leetcode : 343. Integer Break (Medim)](https://leetcode.com/problems/integer-break/description/) - -题目描述:For example, given n = 2, return 1 (2 = 1 + 1); given n = 10, return 36 (10 = 3 + 3 + 4). - -```java -public int integerBreak(int n) { - int[] dp = new int[n + 1]; - dp[1] = 1; - for(int i = 2; i <= n; i++) { - for(int j = 1; j <= i - 1; j++) { - dp[i] = Math.max(dp[i], Math.max(j * dp[i - j], j * (i - j))); - } - } - return dp[n]; -} -``` - -**按平方数来分割整数** - -[Leetcode : 279. Perfect Squares(Medium)](https://leetcode.com/problems/perfect-squares/description/) - -题目描述:For example, given n = 12, return 3 because 12 = 4 + 4 + 4; given n = 13, return 2 because 13 = 4 + 9. - -```java -public int numSquares(int n) { - List squares = new ArrayList<>(); // 存储小于 n 的平方数 - int diff = 3; - while(square <= n) { - squares.add(square); - square += diff; - diff += 2; - } - int[] dp = new int[n + 1]; - for(int i = 1; i <= n; i++) { - int max = Integer.MAX_VALUE; - for(int s : squares) { - if(s > i) break; - max = Math.min(max, dp[i - s] + 1); - } - dp[i] = max; - } - return dp[n]; -} -``` - -**分割整数构成字母字符串** - -[Leetcode : 91. Decode Ways (Medium)](https://leetcode.com/problems/decode-ways/description/) - -题目描述:Given encoded message "12", it could be decoded as "AB" (1 2) or "L" (12). - -```java -public int numDecodings(String s) { - if(s == null || s.length() == 0) return 0; - int n = s.length(); - int[] dp = new int[n + 1]; - dp[0] = 1; - dp[1] = s.charAt(0) == '0' ? 0 : 1; - for(int i = 2; i <= n; i++) { - int one = Integer.valueOf(s.substring(i - 1, i)); - if(one != 0) dp[i] += dp[i - 1]; - if(s.charAt(i - 2) == '0') continue; - int two = Integer.valueOf(s.substring(i - 2, i)); - if(two <= 26) dp[i] += dp[i - 2]; - } - return dp[n]; -} -``` - -### 矩阵路径 - -**矩阵的总路径数** - -[Leetcode : 62. Unique Paths (Medium)](https://leetcode.com/problems/unique-paths/description/) - -题目描述:统计从矩阵左上角到右下角的路径总数,每次只能向左和向下移动。 - -```java -public int uniquePaths(int m, int n) { - int[] dp = new int[n]; - for (int i = 0; i < m; i++) { - for (int j = 0; j < n; j++) { - if(i == 0) dp[j] = 1; - else if(j != 0) dp[j] = dp[j] + dp[j - 1]; - } - } - return dp[n - 1]; -} -``` - -**矩阵的最小路径和** - -[Leetcode : 64. Minimum Path Sum (Medium)](https://leetcode.com/problems/minimum-path-sum/description/) - -题目描述:求从矩阵的左上角到右下角的最小路径和,每次只能向左和向下移动。 - -```java -public int minPathSum(int[][] grid) { - if(grid.length == 0 || grid[0].length == 0) return 0; - int m = grid.length, n = grid[0].length; - int[] dp = new int[n]; - for(int i = 0; i < m; i++) { - for(int j = 0; j < n; j++) { - if(j == 0) dp[0] = dp[0] + grid[i][0]; - else if(i == 0) dp[j] = dp[j - 1] + grid[0][j]; - else dp[j] = Math.min(dp[j - 1], dp[j]) + grid[i][j]; - } - } - return dp[n - 1]; -} -``` - ### 斐波那契数列 **爬楼梯** @@ -1830,7 +1739,6 @@ dp[N] 即为所求。 public int climbStairs(int n) { if(n == 1) return 1; if(n == 2) return 2; - // 前一个楼梯、后一个楼梯 int pre1 = 2, pre2 = 1; for(int i = 2; i < n; i++){ int cur = pre1 + pre2; @@ -1904,31 +1812,32 @@ public int rob(int[] nums) { [Leetcode : 213. House Robber II (Medium)](https://leetcode.com/problems/house-robber-ii/description/) ```java +private int[] dp; + public int rob(int[] nums) { - if(nums == null || nums.length == 0) return 0; + if (nums == null || nums.length == 0) return 0; int n = nums.length; - if(n == 1) return nums[0]; + if (n == 1) return nums[0]; + dp = new int[n]; return Math.max(rob(nums, 0, n - 2), rob(nums, 1, n - 1)); } -private int rob(int[] nums, int s, int e) { - int n = nums.length; - if(e - s == 0) return nums[s]; - if(e - s == 1) return Math.max(nums[s], nums[s + 1]); - int[] dp = new int[n]; - dp[s] = nums[s]; - dp[s + 1] = nums[s + 1]; - dp[s + 2] = nums[s] + nums[s + 2]; - for (int i = s + 3; i <= e; i++) { +private int rob(int[] nums, int first, int last) { + if (last - first == 0) return nums[first]; + if (last - first == 1) return Math.max(nums[first], nums[first + 1]); + dp[first] = nums[first]; + dp[first + 1] = nums[first + 1]; + dp[first + 2] = nums[first] + nums[first + 2]; + for (int i = first + 3; i <= last; i++) { dp[i] = Math.max(dp[i - 2], dp[i - 3]) + nums[i]; } - return Math.max(dp[e], dp[e - 1]); + return Math.max(dp[last], dp[last - 1]); } ``` **信件错排** -题目描述:有 N 个 信 和 信封,它们被打乱,求错误装信的方式数量。 +题目描述:有 N 个 信 和 信封,它们被打乱,求错误装信方式的数量。 定义一个数组 dp 存储错误方式数量,dp[i] 表示前 i 个信和信封的错误方式数量。假设第 i 个信装到第 j 个信封里面,而第 j 个信装到第 k 个信封里面。根据 i 和 k 是否相等,有两种情况: @@ -1981,12 +1890,12 @@ public int lengthOfLIS(int[] nums) { } ``` -以上解法的时间复杂度为 O(n2) ,可以使用二分查找使得时间复杂度降低为 O(nlogn)。定义一个 tails 数组,其中 tails[i] 存储长度为 i + 1 的最长递增子序列的最后一个元素,例如对于数组 [4,5,6,3],有 +以上解法的时间复杂度为 O(n2) ,可以使用二分查找使得时间复杂度降低为 O(nlogn)。定义一个 tails 数组,其中 tails[i] 存储长度为 i + 1 的最长递增子序列的最后一个元素,例如对于数组 [4,5,6,3],有 ```html len = 1 : [4], [5], [6], [3] => tails[0] = 3 len = 2 : [4, 5], [5, 6] => tails[1] = 5 -len = 3 : [4, 5, 6] => tails[2] = 6 +len = 3 : [4, 5, 6] => tails[2] = 6 ``` 对于一个元素 x,如果它大于 tails 数组所有的值,那么把它添加到 tails 后面;如果 tails[i-1] < x <= tails[i],那么更新 tails[i] = x 。 @@ -1998,22 +1907,22 @@ public int lengthOfLIS(int[] nums) { int n = nums.length; int[] tails = new int[n]; int size = 0; - for(int i = 0; i < n; i++){ - int idx = binarySearch(tails, 0, size, nums[i]); - tails[idx] = nums[i]; - if(idx == size) size++; + for (int i = 0; i < n; i++) { + int index = binarySearch(tails, 0, size, nums[i]); + tails[index] = nums[i]; + if (index == size) size++; } return size; } -private int binarySearch(int[] nums, int sIdx, int eIdx, int key){ - while(sIdx < eIdx){ - int mIdx = sIdx + (eIdx - sIdx) / 2; - if(nums[mIdx] == key) return mIdx; - else if(nums[mIdx] > key) eIdx = mIdx; - else sIdx = mIdx + 1; +private int binarySearch(int[] nums, int first, int last, int key) { + while (first < last) { + int mid = first + (last - first) / 2; + if (nums[mid] == key) return mid; + else if (nums[mid] > key) last = mid; + else first = mid + 1; } - return sIdx; + return first; } ``` @@ -2021,6 +1930,19 @@ private int binarySearch(int[] nums, int sIdx, int eIdx, int key){ [Leetcode : 376. Wiggle Subsequence (Medium)](https://leetcode.com/problems/wiggle-subsequence/description/) +```html +Input: [1,7,4,9,2,5] +Output: 6 +The entire sequence is a wiggle sequence. + +Input: [1,17,5,10,13,15,10,5,16,8] +Output: 7 +There are several subsequences that achieve this length. One is [1,17,10,13,10,16,8]. + +Input: [1,2,3,4,5,6,7,8,9] +Output: 2 +``` + 要求:使用 O(n) 时间复杂度求解。 使用两个状态 up 和 down。 @@ -2099,7 +2021,7 @@ public int knapsack(int W, int N, int[] weights, int[] values) { for (int j = W - 1; j >= weights[i]; j--) { dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weights[i]] + values[i]); } - for (int j = weights[i - 1] - 1; j >= 0; j--) { + for (int j = weights[i] - 1; j >= 0; j--) { dp[i][j] = dp[i - 1][j]; } } @@ -2139,6 +2061,14 @@ public int knapsack(int W, int N, int[] weights, int[] values) { [Leetcode : 416. Partition Equal Subset Sum (Medium)](https://leetcode.com/problems/partition-equal-subset-sum/description/) +```html +Input: [1, 5, 11, 5] + +Output: true + +Explanation: The array can be partitioned as [1, 5, 5] and [11]. +``` + 可以看成一个背包大小为 sum/2 的 0-1 背包问题,但是也有不同的地方,这里没有价值属性,并且背包必须被填满。 以下实现使用了空间优化。 @@ -2154,16 +2084,16 @@ public boolean canPartition(int[] nums) { } int W = sum / 2; boolean[] dp = new boolean[W + 1]; - int n = nums.length; - for(int i = 0; i <= W; i++) { - if(nums[0] == i) dp[i] = true; + for (int i = 0; i <= W; i++) { + if (nums[0] == i) { + dp[i] = true; + } } - for(int i = 1; i < n; i++) { - for(int j = W; j >= nums[i]; j--) { + for (int i = 1; i < nums.length; i++) { + for (int j = W; j >= nums[i]; j--) { dp[j] = dp[j] || dp[j - nums[i]]; } } - return dp[W]; } ``` @@ -2185,8 +2115,7 @@ public boolean wordBreak(String s, List wordDict) { dp[0] = true; for (int i = 1; i <= n; i++) { for (String word : wordDict) { - if (word.length() <= i - && word.equals(s.substring(i - word.length(), i))) { + if (word.length() <= i && word.equals(s.substring(i - word.length(), i))) { dp[i] = dp[i] || dp[i - word.length()]; } } @@ -2265,9 +2194,8 @@ Explanation: This are totally 4 strings can be formed by the using of 5 0s and 3 ```java public int findMaxForm(String[] strs, int m, int n) { if (strs == null || strs.length == 0) return 0; - int l = strs.length; int[][] dp = new int[m + 1][n + 1]; - for (int i = 0; i < l; i++) { + for (int i = 0; i < strs.length; i++) { String s = strs[i]; int ones = 0, zeros = 0; for (char c : s.toCharArray()) { @@ -2276,9 +2204,7 @@ public int findMaxForm(String[] strs, int m, int n) { } for (int j = m; j >= zeros; j--) { for (int k = n; k >= ones; k--) { - if (zeros <= j && ones <= k) { - dp[j][k] = Math.max(dp[j][k], dp[j - zeros][k - ones] + 1); - } + dp[j][k] = Math.max(dp[j][k], dp[j - zeros][k - ones] + 1); } } } @@ -2290,20 +2216,30 @@ public int findMaxForm(String[] strs, int m, int n) { [Leetcode : 322. Coin Change (Medium)](https://leetcode.com/problems/coin-change/description/) +```html +Example 1: +coins = [1, 2, 5], amount = 11 +return 3 (11 = 5 + 5 + 1) + +Example 2: +coins = [2], amount = 3 +return -1. +``` + 题目描述:给一些面额的硬币,要求用这些硬币来组成给定面额的钱数,并且使得硬币数量最少。硬币可以重复使用。 这是一个完全背包问题,完全背包问题和 0-1 背包问题在实现上唯一的不同是,第二层循环是从 0 开始的,而不是从尾部开始。 ```java public int coinChange(int[] coins, int amount) { + if (coins == null || coins.length == 0) return 0; + Arrays.sort(coins); int[] dp = new int[amount + 1]; Arrays.fill(dp, amount + 1); dp[0] = 0; for (int i = 1; i <= amount; i++) { - for (int j = 0; j < coins.length; j++) { - if (coins[j] <= i) { - dp[i] = Math.min(dp[i], dp[i - coins[j]] + 1); - } + for (int j = 0; j < coins.length && coins[j] <= i; j++) { + dp[i] = Math.min(dp[i], dp[i - coins[j]] + 1); } } return dp[amount] > amount ? -1 : dp[amount]; @@ -2334,11 +2270,12 @@ Therefore the output is 7. ```java public int combinationSum4(int[] nums, int target) { + if (nums == null || nums.length == 0) return 0; int[] dp = new int[target + 1]; dp[0] = 1; for (int i = 1; i <= target; i++) { for (int j = 0; j < nums.length; j++) { - if(nums[j] <= i) { + if (nums[j] <= i) { dp[i] += dp[i - nums[j]]; } } @@ -2376,7 +2313,7 @@ dp[i, j] = max(dp[i, j-1], prices[j] - prices[jj] + dp[i-1, jj]) { jj in range o ```java public int maxProfit(int k, int[] prices) { int n = prices.length; - if (k >= n/2) { + if (k >= n/2) { // 这种情况下该问题退化为普通的股票交易问题 int maxPro = 0; for (int i = 1; i < n; i++) { if (prices[i] > prices[i-1]) @@ -2402,21 +2339,29 @@ public int maxProfit(int k, int[] prices) { [Leetcode : 303. Range Sum Query - Immutable (Easy)](https://leetcode.com/problems/range-sum-query-immutable/description/) -求区间 i \~ j 的和,可以转换为 sum[j] - sum[i-1],其中 sum[i] 为 0 \~ j 的和。 +```html +Given nums = [-2, 0, 3, -5, 2, -1] + +sumRange(0, 2) -> 1 +sumRange(2, 5) -> -1 +sumRange(0, 5) -> -3 +``` + +求区间 i \~ j 的和,可以转换为 sum[j] - sum[i-1],其中 sum[i] 为 0 \~ i 的和。 ```java class NumArray { - - int[] nums; + private int[] sums; public NumArray(int[] nums) { - for(int i = 1; i < nums.length; i++) - nums[i] += nums[i - 1]; - this.nums = nums; + sums = new int[nums.length]; + for (int i = 0; i < nums.length; i++) { + sums[i] = i == 0 ? nums[0] : sums[i - 1] + nums[i]; + } } public int sumRange(int i, int j) { - return i == 0 ? nums[j] : nums[j] - nums[i - 1]; + return i == 0 ? sums[j] : sums[j] - sums[i - 1]; } } ``` @@ -2425,33 +2370,21 @@ class NumArray { [Leetcode : 53. Maximum Subarray (Easy)](https://leetcode.com/problems/maximum-subarray/description/) -令 sum[i] 为以 num[i] 为结尾的子数组最大的和,可以由 sum[i-1] 得到 sum[i] 的值,如果 sum[i-1] 小于 0,那么以 num[i] 为结尾的子数组不能包含前面的内容,因为加上前面的部分,那么和一定会比 num[i] 还小。 - -```java -public int maxSubArray(int[] nums) { - int n = nums.length; - int[] sum = new int[n]; - sum[0] = nums[0]; - int max = sum[0]; - for(int i = 1; i < n; i++){ - sum[i] = (sum[i-1] > 0 ? sum[i-1] : 0) + nums[i]; - max = Math.max(max, sum[i]); - } - return max; -} +```html +For example, given the array [-2,1,-3,4,-1,2,1,-5,4], +the contiguous subarray [4,-1,2,1] has the largest sum = 6. ``` -空间复杂度可以优化成 O(1) 空间复杂度 - ```java public int maxSubArray(int[] nums) { - int max = nums[0]; - int oldsum = nums[0]; + if (nums == null || nums.length == 0) return 0; + int preSum = nums[0]; + int maxSum = preSum; for (int i = 1; i < nums.length; i++) { - oldsum = (oldsum > 0 ? oldsum: 0) + nums[i]; - max = Math.max(max, oldsum); + preSum = preSum > 0 ? preSum + nums[i] : nums[i]; + maxSum = Math.max(maxSum, preSum); } - return max; + return maxSum; } ``` @@ -2461,25 +2394,25 @@ public int maxSubArray(int[] nums) { ```html A = [1, 2, 3, 4] - return: 3, for 3 arithmetic slices in A: [1, 2, 3], [2, 3, 4] and [1, 2, 3, 4] itself. ``` -对于 (1,2,3,4),它有三种组成递增子区间的方式,而对于 (1,2,3,4,5),它组成递增子区间的方式除了 (1,2,3,4) 的三种外还多了一种,即 (1,2,3,4,5),因此 dp[i] = dp[i - 1] + 1。 +dp[i] 表示以 A[i] 为结尾的等差递增子区间的个数。 + +如果 A[i] - A[i - 1] == A[i - 1] - A[i - 2],表示 [A[i - 2], A[i - 1], A[i]] 是一个等差递增子区间。如果 [A[i - 3], A[i - 2], A[i - 1]] 是一个等差递增子区间,那么 [A[i - 3], A[i - 2], A[i - 1], A[i]] 也是。因此在这个条件下,dp[i] = dp[i-1] + 1。 ```java public int numberOfArithmeticSlices(int[] A) { + if (A == null || A.length == 0) return 0; int n = A.length; int[] dp = new int[n]; - for(int i = 2; i < n; i++) { - if(A[i] - A[i - 1] == A[i - 1] - A[i - 2]) { + for (int i = 2; i < n; i++) { + if (A[i] - A[i - 1] == A[i - 1] - A[i - 2]) { dp[i] = dp[i - 1] + 1; } } int ret = 0; - for(int cnt : dp) { - ret += cnt; - } + for (int cnt : dp) ret += cnt; return ret; } ``` @@ -2490,6 +2423,12 @@ public int numberOfArithmeticSlices(int[] A) { [Leetcode : 583. Delete Operation for Two Strings (Medium)](https://leetcode.com/problems/delete-operation-for-two-strings/description/) +```html +Input: "sea", "eat" +Output: 2 +Explanation: You need one step to make "sea" to "ea" and another step to make "eat" to "ea". +``` + 可以转换为求两个字符串的最长公共子序列问题。 ```java @@ -2499,8 +2438,8 @@ public int minDistance(String word1, String word2) { for (int i = 0; i <= m; i++) { for (int j = 0; j <= n; j++) { if (i == 0 || j == 0) continue; - dp[i][j] = word1.charAt(i - 1) == word2.charAt(j - 1) ? dp[i - 1][j - 1] + 1 - : Math.max(dp[i][j - 1], dp[i - 1][j]); + dp[i][j] = word1.charAt(i - 1) == word2.charAt(j - 1) ? + dp[i - 1][j - 1] + 1 : Math.max(dp[i][j - 1], dp[i - 1][j]); } } return m + n - 2 * dp[m][n]; @@ -2511,6 +2450,137 @@ public int minDistance(String word1, String word2) { [Leetcode : 72. Edit Distance (Hard)](https://leetcode.com/problems/edit-distance/description/) +### 分割整数 + +**分割整数的最大乘积** + +[Leetcode : 343. Integer Break (Medim)](https://leetcode.com/problems/integer-break/description/) + +题目描述:For example, given n = 2, return 1 (2 = 1 + 1); given n = 10, return 36 (10 = 3 + 3 + 4). + +```java +public int integerBreak(int n) { + int[] dp = new int[n + 1]; + dp[1] = 1; + for(int i = 2; i <= n; i++) { + for(int j = 1; j <= i - 1; j++) { + dp[i] = Math.max(dp[i], Math.max(j * dp[i - j], j * (i - j))); + } + } + return dp[n]; +} +``` + +**按平方数来分割整数** + +[Leetcode : 279. Perfect Squares(Medium)](https://leetcode.com/problems/perfect-squares/description/) + +题目描述:For example, given n = 12, return 3 because 12 = 4 + 4 + 4; given n = 13, return 2 because 13 = 4 + 9. + +```java +public int numSquares(int n) { + List squareList = generateSquareList(n); + int[] dp = new int[n + 1]; + for (int i = 1; i <= n; i++) { + int max = Integer.MAX_VALUE; + for (int square : squareList) { + if (square > i) break; + max = Math.min(max, dp[i - square] + 1); + } + dp[i] = max; + } + return dp[n]; +} + +private List generateSquareList(int n) { + List squareList = new ArrayList<>(); + int diff = 3; + int square = 1; + while (square <= n) { + squareList.add(square); + square += diff; + diff += 2; + } + return squareList; +} +``` + +**分割整数构成字母字符串** + +[Leetcode : 91. Decode Ways (Medium)](https://leetcode.com/problems/decode-ways/description/) + +题目描述:Given encoded message "12", it could be decoded as "AB" (1 2) or "L" (12). + +```java +public int numDecodings(String s) { + if(s == null || s.length() == 0) return 0; + int n = s.length(); + int[] dp = new int[n + 1]; + dp[0] = 1; + dp[1] = s.charAt(0) == '0' ? 0 : 1; + for(int i = 2; i <= n; i++) { + int one = Integer.valueOf(s.substring(i - 1, i)); + if(one != 0) dp[i] += dp[i - 1]; + if(s.charAt(i - 2) == '0') continue; + int two = Integer.valueOf(s.substring(i - 2, i)); + if(two <= 26) dp[i] += dp[i - 2]; + } + return dp[n]; +} +``` + +### 矩阵路径 + +**矩阵的总路径数** + +[Leetcode : 62. Unique Paths (Medium)](https://leetcode.com/problems/unique-paths/description/) + +题目描述:统计从矩阵左上角到右下角的路径总数,每次只能向右或者向下移动。 + +

+ +```java +public int uniquePaths(int m, int n) { + int[] dp = new int[n]; + Arrays.fill(dp, 1); + for (int i = 1; i < m; i++) { + for (int j = 1; j < n; j++) { + dp[j] = dp[j] + dp[j - 1]; + } + } + return dp[n - 1]; +} +``` + +**矩阵的最小路径和** + +[Leetcode : 64. Minimum Path Sum (Medium)](https://leetcode.com/problems/minimum-path-sum/description/) + +```html +[[1,3,1], + [1,5,1], + [4,2,1]] +Given the above grid map, return 7. Because the path 1→3→1→1→1 minimizes the sum. +``` + +题目描述:求从矩阵的左上角到右下角的最小路径和,每次只能向左和向下移动。 + +```java +public int minPathSum(int[][] grid) { + if (grid.length == 0 || grid[0].length == 0) return 0; + int m = grid.length, n = grid[0].length; + int[] dp = new int[n]; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (j == 0) dp[0] = dp[0] + grid[i][0]; // 只能从上侧走到该位置 + else if (i == 0) dp[j] = dp[j - 1] + grid[0][j]; // 只能从右侧走到该位置 + else dp[j] = Math.min(dp[j - 1], dp[j]) + grid[i][j]; + } + } + return dp[n - 1]; +} +``` + ### 其它问题 **需要冷却期的股票交易** @@ -2546,26 +2616,16 @@ public int maxProfit(int[] prices) { } ``` -**统计从 0 \~ n 每个数的二进制表示中 1 的个数** - -[Leetcode : 338. Counting Bits (Medium)](https://leetcode.com/problems/counting-bits/description/) - -对于数字 6(110),它可以看成是数字 2(10) 前面加上一个 1 ,因此 dp[i] = dp[i&(i-1)] + 1; - -```java - public int[] countBits(int num) { - int[] ret = new int[num + 1]; - for(int i = 1; i <= num; i++){ - ret[i] = ret[i&(i-1)] + 1; - } - return ret; - } -``` - **一组整数对能够构成的最长链** [Leetcode : 646. Maximum Length of Pair Chain (Medium)](https://leetcode.com/problems/maximum-length-of-pair-chain/description/) +```html +Input: [[1,2], [2,3], [3,4]] +Output: 2 +Explanation: The longest chain is [1,2] -> [3,4] +``` + 对于 (a, b) 和 (c, d) ,如果 b < c,则它们可以构成一条链。 ```java @@ -3028,6 +3088,22 @@ public int[] productExceptSelf(int[] nums) { } ``` +**统计从 0 \~ n 每个数的二进制表示中 1 的个数** + +[Leetcode : 338. Counting Bits (Medium)](https://leetcode.com/problems/counting-bits/description/) + +对于数字 6(110),它可以看成是数字 (10) 前面加上一个 1 ,因此 dp[i] = dp[i&(i-1)] + 1; + +```java +public int[] countBits(int num) { + int[] ret = new int[num + 1]; + for(int i = 1; i <= num; i++){ + ret[i] = ret[i&(i-1)] + 1; + } + return ret; +} +``` + # 数据结构相关 ## 栈和队列 @@ -3567,9 +3643,9 @@ Input: nums = [1,2,2,4] Output: [2,3] ``` -最直接的方法是先对数组进行排序,这种方法时间复杂度为 O(nlogn).本题可以以 O(n) 的时间复杂度、O(1) 空间复杂度来求解。 +最直接的方法是先对数组进行排序,这种方法时间复杂度为 O(nlogn)。本题可以以 O(n) 的时间复杂度、O(1) 空间复杂度来求解。 -主要思想是让通过交换数组元素,使得数组上的元素在正确的位置上 +主要思想是让通过交换数组元素,使得数组上的元素在正确的位置上。 遍历数组,如果第 i 位上的元素不是 i + 1 ,那么就交换第 i 位 和 nums[i] - 1 位上的元素,使得 num[i] - 1 的元素为 nums[i] ,也就是该位的元素是正确的。交换操作需要循环进行,因为一次交换没办法使得第 i 位上的元素是正确的。但是要交换的两个元素可能就是重复元素,那么循环就可能永远进行下去,终止循环的方法是加上 nums[i] != nums[nums[i] - 1 条件。 @@ -4235,9 +4311,10 @@ private TreeNode toBST(int[] nums, int sIdx, int eIdx){ **两节点的最长路径** +[Leetcode : 543. Diameter of Binary Tree (Easy)](https://leetcode.com/problems/diameter-of-binary-tree/description/) + ```html Input: - 1 / \ 2 3 @@ -4247,8 +4324,6 @@ Input: Return 3, which is the length of the path [4,2,1,3] or [5,2,1,3]. ``` -[Leetcode : 543. Diameter of Binary Tree (Easy)](https://leetcode.com/problems/diameter-of-binary-tree/description/) - ```java private int max = 0; @@ -4931,7 +5006,7 @@ class MapSum { **1. 基本原理** -0s 表示 一串 0 ,1s 表示一串 1。 +0s 表示一串 0 ,1s 表示一串 1。 ``` x ^ 0s = x x & 0s = 0 x | 0s = x @@ -4941,7 +5016,7 @@ x ^ x = 0 x & x = x x | x = x ① 利用 x ^ 1s = \~x 的特点,可以将位级表示翻转;利用 x ^ x = 0 的特点,可以将三个数中重复的两个数去除,只留下另一个数; ② 利用 x & 0s = 0 和 x & 1s = x 的特点,可以实现掩码操作。一个数 num 与 mask :00111100 进行位与操作,只保留 num 中与 mask 的 1 部分相对应的位; -③ 利用 x | 0s = x 和 x | 1s = 1s 的特点,可以实现设置操作。一个数 num 与 mask:00111100 进行位或操作,将 num 中与 mask 的 1 部分相对应的位都设置为 1 。 +③ 利用 x | 0s = x 和 x | 1s = 1s 的特点,可以实现设值操作。一个数 num 与 mask:00111100 进行位或操作,将 num 中与 mask 的 1 部分相对应的位都设置为 1 。 \>\> n 为算术右移,相当于除以 2n; \>\>\> n 为无符号右移,左边会补上 0。 @@ -4949,7 +5024,7 @@ x ^ x = 0 x & x = x x | x = x n&(n-1) 该位运算是去除 n 的位级表示中最低的那一位。例如对于二进制表示 10110 **100** ,减去 1 得到 10110**011**,这两个数相与得到 10110**000**。 -n-n&(\~n+1) 概运算是去除 n 的位级表示中最高的那一位。 +n-n&(\~n+1) 运算是去除 n 的位级表示中最高的那一位。 n&(-n) 该运算得到 n 的位级表示中最低的那一位。-n 得到 n 的反码加 1,对于二进制表示 10110 **100** ,-n 得到 01001**100**,相与得到 00000**100** @@ -5016,9 +5091,9 @@ num & (~((1 << (i+1)) - 1)); **4. Java 中的位操作** ```html -static int Integer.bitCount() // 统计 1 的数量 -static int Integer.highestOneBit() // 获得最高位 -static String toBinaryString(int i) // 转换位二进制表示的字符串 +static int Integer.bitCount(); // 统计 1 的数量 +static int Integer.highestOneBit(); // 获得最高位 +static String toBinaryString(int i); // 转换为二进制表示的字符串 ``` **统计两个数的二进制表示有多少位不同** @@ -5073,13 +5148,13 @@ b = a ^ b; a = a ^ b; ``` -将 c = a ^ b,那么 b ^ c = b ^ b ^ a = a,a ^ c = a ^ a ^ b = b。 +令 c = a ^ b,那么 b ^ c = b ^ b ^ a = a,a ^ c = a ^ a ^ b = b。 **判断一个数是不是 4 的 n 次方** [Leetcode : 342. Power of Four (Easy)](https://leetcode.com/problems/power-of-four/) -该数二进制表示有且只有一个奇数位为 1 ,其余的都为 0 ,例如 16 : 10000。可以每次把 1 向左移动 2 位,就能构造出这种数字,然后比较构造出来的数与要判断的数是否相同。 +该数二进制表示有且只有一个奇数位为 1 ,其余的都为 0 ,例如 16 :10000。可以每次把 1 向左移动 2 位,就能构造出这种数字,然后比较构造出来的数与要判断的数是否相同。 ```java public boolean isPowerOfFour(int num) { @@ -5092,14 +5167,6 @@ public boolean isPowerOfFour(int num) { } ``` -也可以用 Java 的 Integer.toString() 方法将该数转换为 4 进制形式的字符串,然后判断字符串是否以 1 开头。 - -```java -public boolean isPowerOfFour(int num) { - return Integer.toString(num, 4).matches("10*"); -} -``` - **判断一个数是不是 2 的 n 次方** [Leetcode : 231. Power of Two (Easy)](https://leetcode.com/problems/power-of-two/description/) diff --git a/notes/Linux.md b/notes/Linux.md index c592bf24..68f3ad78 100644 --- a/notes/Linux.md +++ b/notes/Linux.md @@ -154,16 +154,16 @@ Linux 发行版是 Linux 内核及各种应用软件的集成版本。 ## VIM 三个模式 -

+- 一般指令模式(Command mode):进入 VIM 的默认模式,可以用于移动游标查看内容; +- 编辑模式(Insert mode):按下 "i" 等按键之后进入,可以对文本进行编辑; +- 指令列模式(Bottom-line mode):按下 ":" 按键之后进入,用于保存退出等操作。 -- 一般指令模式:进入 VIM 的默认模式,可以用于移动游标查看内容; -- 编辑模式:按下 "i" 等按键之后进入,可以对文本进行编辑; -- 指令列模式:按下 ":" 按键之后进入,用于保存退出等操作。 +

在指令列模式下,有以下命令用于离开或者保存文件。 | 命令 | 作用 | -| -- | -- | +| :--: | -- | | :w | 写入磁盘| | :w! | 当文件为只读时,强制写入磁盘。到底能不能写入,与用户对该文件的权限有关 | | :q | 离开 | @@ -202,7 +202,7 @@ GPT 第 1 个区块记录了 MBR,紧接着是 33 个区块记录分区信息 GPT 没有扩展分区概念,都是主分区,最多可以分 128 个分区。 -

+

## 开机检测程序 @@ -321,7 +321,7 @@ UEFI 相比于 BIOS 来说功能更为全面,也更为安全。 - /usr (unix software resource):所有系统默认软件都会安装到这个目录; - /var (variable):存放系统或程序运行过程中的数据文件。 -

+

## 文件时间 @@ -469,7 +469,7 @@ find 可以使用文件的属性和权限进行搜索。 # find filename [option] ``` -**(一)与时间有关的选项** +(一)与时间有关的选项 ```html -mtime n :列出在 n 天前的那一天修改过内容的文件 @@ -480,9 +480,9 @@ find 可以使用文件的属性和权限进行搜索。 +4、4 和 -4 的指示的时间范围如下: -

+

-**(二)与文件拥有者和所属群组有关的选项** +(二)与文件拥有者和所属群组有关的选项 ```html -uid n @@ -493,7 +493,7 @@ find 可以使用文件的属性和权限进行搜索。 -nogroup:搜索所属群组不存在于 /etc/group 的文件 ``` -**(三)与文件权限和名称有关的选项** +(三)与文件权限和名称有关的选项 ```html -name filename @@ -516,21 +516,19 @@ find 可以使用文件的属性和权限进行搜索。 2. inode:一个文件占用一个 inode,记录文件的属性,同时记录此文件的内容所在的 block 号码; 3. block:记录文件的内容,文件太大时,会占用多个 block。 +

+ 当要读取一个文件的内容时,先在 inode 中去查找文件内容所在的所有 block,然后把所有 block 的内容读出来。 磁盘碎片是指一个文件内容所在的 block 过于分散。 -Ext2 文件系统使用了上述的文件结构,并在此之上加入了 block 群组的概念,也就是将一个文件系统划分为多个 block 群组,方便管理。 - -

- ## inode Ext2 文件系统支持的 block 大小有 1k、2k 和 4k 三种,不同的 block 大小限制了单一文件的大小。而每个 inode 大小是固定为 128 bytes。 inode 中记录了文件内容所在的 block,但是每个 block 非常小,一个大文件随便都需要几十万的 block。而一个 inode 大小有限,无法直接引用这么多 block。因此引入了间接、双间接、三间接引用。间接引用是指,让 inode 记录的引用 block 块当成 inode 用来记录引用信息。 -

+

inode 具体包含以下信息: @@ -561,7 +559,7 @@ inode 具体包含以下信息: 删除任意一个条目,文件还是存在,只要引用数量不为 0。 -有以下限制:不能跨越 File System;不能对目录进行链接。 +有以下限制:不能跨越 File System、不能对目录进行链接。 ```html # ln /etc/crontab . @@ -636,7 +634,7 @@ $ bzip2 [-cdkzv#] filename 提供比 bzip2 更佳的压缩比。 -可以看到,gzip、bzip2、xz 的压缩比不断优化。不过要注意,压缩比越高,压缩的时间也越长。 +可以看到,gzip、bzip2、xz 的压缩比不断优化。不过要注意的是,压缩比越高,压缩的时间也越长。 查看命令:xzcat、xzmore、xzless、xzgrep。 @@ -932,7 +930,7 @@ $ grep -n 'the' regular_express.txt 18:google is the best tools for search keyword ``` -因为 { 与 } 的符号在 shell 是有特殊意义的,因此必须要使用转义字符进行转义。 +因为 { 和 } 在 shell 是有特殊意义的,因此必须要使用转义字符进行转义。 ```html $ grep -n 'go\{2,5\}g' regular_express.txt @@ -1026,7 +1024,7 @@ daemon 2 示例三:查看特定的进程 -```html +``` # ps aux | grep threadx ``` @@ -1060,16 +1058,17 @@ daemon 2 ## 进程状态 +

+ + | 状态 | 说明 | | :---: | --- | | R | running or runnable (on run queue) | -| D | uninterruptible sleep (usually IO) | +| D | uninterruptible sleep (usually IO) | | S | interruptible sleep (waiting for an event to complete) | | Z | defunct/zombie, terminated but not reaped by its parent | | T | stopped, either by a job control signal or because it is being traced| -

- ## SIGCHILD 当一个子进程改变了它的状态时:停止运行,继续运行或者退出,有两件事会发生在父进程中: @@ -1077,13 +1076,13 @@ daemon 2 - 得到 SIGCHLD 信号; - 阻塞的 waitpid(2)(或者 wait)调用会返回。 -

+

## 孤儿进程和僵死进程 ### 1. 孤儿进程 -一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被 init 进程(进程号为 1)所收养,并由 init 进程对它们完成状态收集工作。 +一个父进程退出,而它的一个或多个子进程还在运行,那么这些子进程将成为孤儿进程。孤儿进程将被 init 进程(进程号为 1)所收养,并由 init 进程对它们完成状态收集工作。 由于孤儿进程会被 init 进程收养,所以孤儿进程不会对系统造成危害。 @@ -1118,7 +1117,7 @@ I/O Multiplexing 又被称为 Event Driven I/O,它可以让单个进程具有 同步异步是获知 I/O 完成的方式,同步需要时刻关心 I/O 是否已经完成,异步无需主动关心,在 I/O 完成时它会收到通知。 -

+

### 1. 同步-阻塞 @@ -1126,31 +1125,31 @@ I/O Multiplexing 又被称为 Event Driven I/O,它可以让单个进程具有 应该注意到,在阻塞的过程中,其他程序还可以执行,因此阻塞不意味着整个操作系统都被阻塞。因为其他程序还可以执行,因此不消耗 CPU 时间,这种模型的执行效率会比较高。 -

+

### 2. 同步-非阻塞 -非阻塞意味着用户程序在执行系统调用后还可以执行,内核并不是马上执行完 I/O,而是以一个错误码来告知用户程序 I/O 还未完成。为了获得 I/O 完成事件,用户程序必须调用多次系统调用去询问内核,甚至是忙等,也就是在一个循环里面一直询问并等待。 +非阻塞意味着用户程序在执行系统调用后还可以继续执行,内核并不是马上执行完 I/O,而是以一个错误码来告知用户程序 I/O 还未完成。为了获得 I/O 完成事件,用户程序必须调用多次系统调用去询问内核,甚至是忙等,也就是在一个循环里面一直询问并等待。 由于 CPU 要处理更多的用户程序的询问,因此这种模型的效率是比较低的。 -

+

### 3. 异步-阻塞 这是 I/O 复用使用的一种模式,通过使用 select,它可以监听多个 I/O 事件,当这些事件至少有一个发生时,用户程序会收到通知。 -

+

### 4. 异步-非阻塞 -该模式下,I/O 操作会立即返回,之后可以处理其它操作,并且在 I/O 完成时会收到一个通知,此时会中断正在处理的操作,然后完成 I/O 事务。 +该模式下,I/O 操作会立即返回,之后可以处理其它操作,并且在 I/O 完成时会收到一个通知,此时会中断正在处理的操作,然后继续之前的操作。 -

+

## select poll epoll -这三个都是 I/O 多路复用的具体实现,select 出现的最早,之后是 poll,再是 epoll。可以说,新出现的实现是为了修复旧实现的不足。 +这三个都是 I/O 多路复用的具体实现,select 出现的最早,之后是 poll,再是 epoll。 ### 1. select @@ -1321,7 +1320,7 @@ select 和 poll 方式中,进程只有在调用一定的方法后,内核才 新版本的 epoll_create(int size) 参数 size 不起任何作用,在旧版本的 epoll 中如果描述符的数量大于 size,不保证服务质量。 -epoll_ct() 执行一次系统调用,用于向内核注册新的描述符或者是改变某个文件描述符的状态。已注册的描述符在内核中会被维护在一棵红黑树上,通过回调函数内核会将 I/O 准备好的描述符加入到一个链表中管理。 +epoll_ctl() 执行一次系统调用,用于向内核注册新的描述符或者是改变某个文件描述符的状态。已注册的描述符在内核中会被维护在一棵红黑树上,通过回调函数内核会将 I/O 准备好的描述符加入到一个链表中管理。 epoll_wait() 取出在内核中通过链表维护的 I/O 准备好的描述符,将他们从内核复制到程序中,不需要像 select/poll 对注册的所有描述符遍历一遍。 @@ -1396,3 +1395,4 @@ poll 没有最大描述符数量的限制,如果平台支持应该采用 poll - [poll vs select vs event-based](https://daniel.haxx.se/docs/poll-vs-select.html) - [Linux 之守护进程、僵死进程与孤儿进程](http://liubigbin.github.io/2016/03/11/Linux-%E4%B9%8B%E5%AE%88%E6%8A%A4%E8%BF%9B%E7%A8%8B%E3%80%81%E5%83%B5%E6%AD%BB%E8%BF%9B%E7%A8%8B%E4%B8%8E%E5%AD%A4%E5%84%BF%E8%BF%9B%E7%A8%8B/) - [Linux process states](https://idea.popcount.org/2012-12-11-linux-process-states/) +- [GUID Partition Table](https://en.wikipedia.org/wiki/GUID_Partition_Table) diff --git a/notes/MySQL.md b/notes/MySQL.md index fca2798f..3588d9f9 100644 --- a/notes/MySQL.md +++ b/notes/MySQL.md @@ -14,12 +14,11 @@ * [索引优化](#索引优化) * [B-Tree 和 B+Tree 原理](#b-tree-和-b+tree-原理) * [四、查询性能优化](#四查询性能优化) -* [五、分库与分表](#五分库与分表) - * [原因](#原因) - * [实现方式](#实现方式) - * [Merge 存储引擎](#merge-存储引擎) +* [五、切分](#五切分) + * [垂直切分](#垂直切分) + * [水平切分](#水平切分) + * [切分的选择](#切分的选择) * [存在的问题](#存在的问题) - * [分表与分区的不同](#分表与分区的不同) * [六、故障转移和故障恢复](#六故障转移和故障恢复) * [参考资料](#参考资料) @@ -121,13 +120,17 @@ MySQL 提供了 FROM_UNIXTIME() 函数把 UNIX 时间戳转换为日期,并提 ## 索引分类 -### 1. B-Tree 索引 +### 1. B+Tree 索引 -B-Tree 索引是大多数 MySQL 存储引擎的默认索引类型。 +

+ +《高性能 MySQL》一书使用 B-Tree 进行描述,其实从技术上来说这种索引是 B+Tree。 + +B+Tree 索引是大多数 MySQL 存储引擎的默认索引类型。 因为不再需要进行全表扫描,只需要对树进行搜索即可,因此查找速度快很多。 -可以指定多个列作为索引列,多个索引列共同组成键。B-Tree 索引适用于全键值、键值范围和键前缀查找,其中键前缀查找只适用于最左前缀查找。 +可以指定多个列作为索引列,多个索引列共同组成键。B+Tree 索引适用于全键值、键值范围和键前缀查找,其中键前缀查找只适用于最左前缀查找。 除了用于查找,还可以用于排序和分组。 @@ -139,7 +142,7 @@ B-Tree 索引是大多数 MySQL 存储引擎的默认索引类型。 在 MySQL 中只有 Memory 引擎显式支持哈希索引。 -InnoDB 引擎有一个特殊的功能叫“自适应哈希索引”,当某个索引值被使用的非常频繁时,会在 B-Tree 索引之上再创建一个哈希索引,这样就让 B-Tree 索引具有哈希索引的一些优点,比如快速的哈希查找。 +InnoDB 引擎有一个特殊的功能叫“自适应哈希索引”,当某个索引值被使用的非常频繁时,会在 B+Tree 索引之上再创建一个哈希索引,这样就让 B+Tree 索引具有哈希索引的一些优点,比如快速的哈希查找。 限制:哈希索引只包含哈希值和行指针,而不存储字段值,所以不能使用索引中的值来避免读取行。不过,访问内存中的行的速度很快,所以大部分情况下这一点对性能影响并不明显;无法用于分组与排序;只支持精确查找,无法用于部分查找和范围查找;如果哈希冲突很多,查找速度会变得很慢。 @@ -179,7 +182,7 @@ SELECT actor_id FROM sakila.actor WHERE actor_id + 1 = 5; 对于 BLOB、TEXT 和 VARCHAR 类型的列,必须使用前缀索引,只索引开始的部分字符。 -对于前缀长度的选取需要根据 **索引选择性** 来确定:不重复的索引值和记录总数的比值。选择性越高,查询效率也越高。最大值为 1 ,此时每个记录都有唯一的索引与其对应。 +对于前缀长度的选取需要根据 **索引选择性** 来确定:不重复的索引值和记录总数的比值。选择性越高,查询效率也越高。最大值为 1,此时每个记录都有唯一的索引与其对应。 ### 3. 多列索引 @@ -209,7 +212,7 @@ customer_id_selectivity: 0.0373 ### 5. 聚簇索引 -

+

聚簇索引并不是一种索引类型,而是一种数据存储方式。 @@ -217,12 +220,12 @@ customer_id_selectivity: 0.0373 因为无法把数据行存放在两个不同的地方,所以一个表只能有一个聚簇索引。 -#### 优点 +**优点** 1. 可以把相关数据保存在一起,减少 I/O 操作; 2. 因为数据保存在 B-Tree 中,因此数据访问更快。 -#### 缺点 +**缺点** 1. 聚簇索引最大限度提高了 I/O 密集型应用的性能,但是如果数据全部放在内存,就没必要用聚簇索引。 2. 插入速度严重依赖于插入顺序,按主键的顺序插入是最快的。 @@ -234,7 +237,7 @@ customer_id_selectivity: 0.0373 索引包含所有需要查询的字段的值。 -#### 优点 +**优点** 1. 因为索引条目通常远小于数据行的大小,所以若只读取索引,能大大减少数据访问量。 2. 一些存储引擎(例如 MyISAM)在内存中只缓存索引,而数据依赖于操作系统来缓存。因此,只访问索引可以不使用系统调用(通常比较费时)。 @@ -277,9 +280,9 @@ B-Tree 是满足下列条件的数据结构: 红黑树等数据结构也可以用来实现索引,但是文件系统及数据库系统普遍采用 B-/+Tree 作为索引结构。 -页是计算机管理存储器的逻辑块,硬件及操作系统往往将主存和磁盘存储区分割为连续的大小相等的块,每个存储块称为一页(在许多操作系统中,页得大小通常为 4k),主存和磁盘以页为单位交换数据。 +页是计算机管理存储器的逻辑块,硬件及操作系统往往将主存和磁盘存储区分割为连续的大小相等的块,每个存储块称为一页(在许多操作系统中,页的大小通常为 4k),主存和磁盘以页为单位交换数据。 -一般来说,索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储的磁盘上。为了减少磁盘 I/O,磁盘往往不是严格按需读取,而是每次都会预读。这样做的理论依据是计算机科学中著名的局部性原理:当一个数据被用到时,其附近的数据也通常会马上被使用。数据库系统的设计者巧妙利用了磁盘预读原理,将一个节点的大小设为等于一个页,这样每个节点只需要一次 I/O 就可以完全载入。B-Tree 中一次检索最多需要 h-1 次 I/O(根节点常驻内存),渐进复杂度为 O(h)=O(logdN)。一般实际应用中,出度 d 是非常大的数字,通常超过 100,因此 h 非常小(通常不超过 3)。而红黑树这种结构,h 明显要深的多。并且于逻辑上很近的节点(父子)物理上可能很远,无法利用局部性,效率明显比 B-Tree 差很多。 +一般来说,索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储的磁盘上。为了减少磁盘 I/O,磁盘往往不是严格按需读取,而是每次都会预读。这样做的理论依据是计算机科学中著名的局部性原理:当一个数据被用到时,其附近的数据也通常会马上被使用。数据库系统的设计者巧妙利用了磁盘预读原理,将一个节点的大小设为等于一个页,这样每个节点只需要一次 I/O 就可以完全载入。B-Tree 中一次检索最多需要 h-1 次 I/O(根节点常驻内存),渐进复杂度为 O(h)=O(logdN)。一般实际应用中,出度 d 是非常大的数字,通常超过 100,因此 h 非常小(通常不超过 3)。而红黑树这种结构,h 明显要深的多。并且于逻辑上很近的节点(父子)物理上可能很远,无法利用局部性,效率明显比 B-Tree 差很多。 B+Tree 更适合外存索引,原因和内节点出度 d 有关。由于 B+Tree 内节点去掉了 data 域,因此可以拥有更大的出度,拥有更好的性能。 @@ -318,6 +321,7 @@ SELECT * FROM sakila.film_actor WHERE film_id = 1; ```sql DELEFT FROM messages WHERE create < DATE_SUB(NOW(), INTERVAL 3 MONTH); ``` + ```sql rows_affected = 0 do { @@ -326,32 +330,25 @@ do { } while rows_affected > 0 ``` -# 五、分库与分表 +# 五、切分 -## 原因 随着时间和业务的发展,数据库中的表会越来越多,并且表中的数据量也会越来越大,那么读写操作的开销也会随着增大。 -## 实现方式 - -### 1. 垂直切分 +## 垂直切分 将表按功能模块、关系密切程度划分出来,部署到不同的库上。例如,我们会建立商品数据库 payDB、用户数据库 userDB 等,分别用来存储项目与商品有关的表和与用户有关的表。 -### 2. 水平切分 +## 水平切分 把表中的数据按照某种规则存储到多个结构相同的表中,例如按 id 的散列值、性别等进行划分。 -### 3. 切分的选择 +## 切分的选择 如果数据库中的表太多,并且项目各项业务逻辑清晰,那么垂直切分是首选。 如果数据库的表不多,但是单表的数据量很大,应该选择水平切分。 -## Merge 存储引擎 - -该存储引擎支持分表。 - ## 存在的问题 ### 1. 事务问题 @@ -366,9 +363,6 @@ do { 最显而易见的就是数据的定位问题和数据的增删改查的重复执行问题,这些都可以通过应用程序解决,但必然引起额外的逻辑运算。 -## 分表与分区的不同 - -分表,就是将一张表分成多个小表,这些小表拥有不同的表名;而分区是将一张表的数据分为多个区块,这些区块可以存储在同一个磁盘上,也可以存储在不同的磁盘上,这种方式下表仍然只有一个。 # 六、故障转移和故障恢复 @@ -386,15 +380,14 @@ do { 通过代理,可以路由流量到可以使用的服务器上。 -

- ### 在应用中处理故障转移 将故障转移整合到应用中可能导致应用变得太过笨拙。 # 参考资料 -- 高性能 MySQL +- BaronScbwartz, PeterZaitsev, VadimTkacbenko, 等. 高性能 MySQL[M]. 电子工业出版社, 2013. +- [How Sharding Works](https://medium.com/@jeeyoungk/how-sharding-works-b4dec46b3f6) - [MySQL 索引背后的数据结构及算法原理 ](http://blog.codinglabs.org/articles/theory-of-mysql-index.html) - [20+ 条 MySQL 性能优化的最佳经验 ](https://www.jfox.info/20-tiao-mysql-xing-nen-you-hua-de-zui-jia-jing-yan.html) - [数据库为什么分库分表?mysql的分库分表方案](https://www.i3geek.com/archives/1108) diff --git a/notes/Redis.md b/notes/Redis.md index b882428b..9e5bfa61 100644 --- a/notes/Redis.md +++ b/notes/Redis.md @@ -53,12 +53,12 @@ Redis 支持很多特性,例如将内存中的数据持久化到硬盘中, # 二、五种基本类型 | 数据类型 | 可以存储的值 | 操作 | -| -- | -- | -- | +| :--: | :--: | :--: | | STRING | 字符串、整数或者浮点数 | 对整个字符串或者字符串的其中一部分执行操作
对整数和浮点数执行自增或者自减操作 | | LIST | 链表 | 从两端压入或者弹出元素
读取单个或者多个元素
进行修剪,只保留一个范围内的元素 | | SET | 无序集合 | 添加、获取、移除单个元素
检查一个元素是否存在于集合中
计算交集、并集、差集
从集合里面随机获取元素 | | HASH | 包含键值对的无序散列表 | 添加、获取、移除单个键值对
获取所有键值对
检查某个键是否存在| -| ZSET | 有序集合 | 添加、获取、删除元素个元素
根据分值范围或者成员来获取元素
计算一个键的排名 | +| ZSET | 有序集合 | 添加、获取、删除元素
根据分值范围或者成员来获取元素
计算一个键的排名 | > [What Redis data structures look like](https://redislabs.com/ebook/part-1-getting-started/chapter-1-getting-to-know-redis/1-2-what-redis-data-structures-look-like/) @@ -235,16 +235,20 @@ Redis 是内存型数据库,为了保证数据在断电后不会丢失,需 可以将快照复制到其它服务器从而创建具有相同数据的服务器副本。 -如果系统发生故障,将会丢失最后一次创建快照之后的数据。并且如果数据量很大,保存快照的时间也会很长。 +如果系统发生故障,将会丢失最后一次创建快照之后的数据。 + +如果数据量很大,保存快照的时间会很长。 ## 2. AOF 持久化 AOF 持久化将写命令添加到 AOF 文件(Append Only File)的末尾。 -对硬盘的文件进行写入时,写入的内容首先会被存储到缓冲区,然后由操作系统决定什么时候将该内容同步到硬盘,用户可以调用 file.flush() 方法请求操作系统尽快将缓冲区存储的数据同步到硬盘。因此将写命令添加到 AOF 文件时,要根据需求来保证何时将添加的数据同步到硬盘上,有以下同步选项: +对硬盘的文件进行写入时,写入的内容首先会被存储到缓冲区,然后由操作系统决定什么时候将该内容同步到硬盘,用户可以调用 file.flush() 方法请求操作系统尽快将缓冲区存储的数据同步到硬盘。 + +将写命令添加到 AOF 文件时,要根据需求来保证何时将添加的数据同步到硬盘上,有以下同步选项: | 选项 | 同步频率 | -| -- | -- | +| :--: | :--: | | always | 每个写命令都同步 | | everysec | 每秒同步一次 | | no | 让操作系统来决定何时同步 | @@ -361,7 +365,7 @@ def main(): 从事件处理的角度来看,服务器运行流程如下: -

+

# 十一、Redis 与 Memcached 的区别 @@ -391,26 +395,26 @@ Memcached 将内存分割成特定长度的块来存储数据,以完全解决 ## 缓存 -适用 Redis 作为缓存,将热点数据放到内存中。 +将热点数据放到内存中。 ## 消息队列 -Redis 的 List 类型是双向链表,很适合用于消息队列。 +List 类型是双向链表,很适合用于消息队列。 ## 计数器 -Redis 这种内存数据库才能支持计数器的频繁读写操作。 +Redis 这种内存数据库能支持计数器频繁的读写操作。 ## 好友关系 -使用 set 类型的交集很容易就可以知道两个用户的共同好友。 +使用 Set 类型的交集操作很容易就可以知道两个用户的共同好友。 # 十三、数据淘汰策略 可以设置内存最大使用量,当内存使用量超过时施行淘汰策略,具体有 6 种淘汰策略。 | 策略 | 描述 | -| -- | -- | +| :--: | :--: | | volatile-lru | 从已设置过期时间的数据集中挑选最近最少使用的数据淘汰 | | volatile-ttl | 从已设置过期时间的数据集中挑选将要过期的数据淘汰 | |volatile-random | 从已设置过期时间的数据集中任意选择数据淘汰 | @@ -452,8 +456,8 @@ Redis 没有关系型数据库中的表这一概念来将同类型的数据存 # 参考资料 -- Redis 实战 -- Reids 设计与实现 +- Carlson J L. Redis in Action[J]. Media.johnwiley.com.au, 2013. +- 黄健宏. Redis 设计与实现 [M]. 机械工业出版社, 2014. - [REDIS IN ACTION](https://redislabs.com/ebook/foreword/) - [论述 Redis 和 Memcached 的差异](http://www.cnblogs.com/loveincode/p/7411911.html) - [Redis 3.0 中文版- 分片](http://wiki.jikexueyuan.com/project/redis-guide) diff --git a/notes/SQL.md b/notes/SQL.md index 651c784a..a1bf2937 100644 --- a/notes/SQL.md +++ b/notes/SQL.md @@ -22,6 +22,7 @@ * [二十一、事务处理](#二十一事务处理) * [二十二、字符集](#二十二字符集) * [二十三、权限管理](#二十三权限管理) +* [参考资料](#参考资料) @@ -174,7 +175,7 @@ ORDER BY col1 DESC, col2 ASC; # 九、过滤 -不进行过滤的数据非常大,导致通过网络传输了很多多余的数据,从而浪费了网络带宽。因此尽量使用 SQL 语句来过滤不必要的数据,而不是传输所有的数据到客户端中然后由客户端进行过滤。 +不进行过滤的数据非常大,导致通过网络传输了多余的数据,从而浪费了网络带宽。因此尽量使用 SQL 语句来过滤不必要的数据,而不是传输所有的数据到客户端中然后由客户端进行过滤。 ```sql SELECT * @@ -189,7 +190,7 @@ WHERE col IS NULL; | = < > | 等于 小于 大于 | | <> != | 不等于 | | <= !> | 小于等于 | -| >= !< | 大于等于 | +| >= !< | 大于等于 | | BETWEEN | 在两个值之间 | | IS NULL | 为NULL值 | @@ -205,9 +206,9 @@ WHERE col IS NULL; 通配符也是用在过滤语句中,但它只能用于文本字段。 -- **%** 匹配 >=0 个任意字符,类似于 \*; +- **%** 匹配 >=0 个任意字符; -- **\_** 匹配 ==1 个任意字符,类似于 \.; +- **\_** 匹配 ==1 个任意字符; - **[ ]** 可以匹配集合内的字符,例如 [ab] 将匹配字符 a 或者 b。用脱字符 ^ 可以对其进行否定,也就是不匹配集合内的字符。 @@ -316,7 +317,7 @@ mysql> SELECT NOW(); AVG() 会忽略 NULL 行。 -使用 DISTINCT 可以汇总函数值汇总不同的值。 +使用 DISTINCT 可以让汇总函数值汇总不同的值。 ```sql SELECT AVG(DISTINCT col1) AS avg_col @@ -369,7 +370,7 @@ ORDER BY num; 可以将子查询的结果作为 WHRER 语句的过滤条件: -``` +```sql SELECT * FROM mytable1 WHERE col1 IN (SELECT col2 @@ -435,7 +436,7 @@ where department = ( 自连接版本 ```sql -select name +select e2.name from employee as e1, employee as e2 where e1.department = e2.department and e1.name = "Jim"; @@ -621,7 +622,7 @@ MySQL 不允许在触发器中使用 CALL 语句 ,也就是不能调用存储 3. 提交(commit)指将未存储的 SQL 语句结果写入数据库表; 4. 保留点(savepoint)指事务处理中设置的临时占位符(placeholder),你可以对它发布回退(与回退整个事务处理不同)。 -不能回退 SELECT 语句,回退 SELECT 语句也没意义;也不能回退 CRETE 和 DROP 语句。 +不能回退 SELECT 语句,回退 SELECT 语句也没意义;也不能回退 CREATE 和 DROP 语句。 MySQL 的事务提交默认是隐式提交,也就是每执行一条语句就把这条语句当成一个事务然后进行提交。当出现 START TRANSACTION 语句时,会关闭隐式提交;当 COMMIT 或 ROLLBACK 语句执行后,事务会自动关闭,重新恢复隐式提交。 @@ -704,8 +705,6 @@ SHOW GRANTS FOR myuser; GRANT SELECT, INSERT ON mydatabase.* TO myuser; ``` -

- 账户用 username@host 的形式定义,username@% 使用的是默认主机名。 ## 删除权限 @@ -730,3 +729,6 @@ GRANT 和 REVOKE 可在几个层次上控制访问权限: SET PASSWROD FOR myuser = Password('newpassword'); ``` +# 参考资料 + +- BenForta. SQL 必知必会 [M]. 人民邮电出版社, 2013. diff --git a/notes/一致性协议.md b/notes/一致性协议.md index e2b2486d..8913ff3b 100644 --- a/notes/一致性协议.md +++ b/notes/一致性协议.md @@ -24,7 +24,7 @@ Two-phase Commit(2PC)。

-需要注意的是,在准备阶段,参与者执行了事务,但是还未提交。只有在提交阶段协接收到协调者发来的通知后,才进行提交或者回滚。 +需要注意的是,在准备阶段,参与者执行了事务,但是还未提交。只有在提交阶段接收到协调者发来的通知后,才进行提交或者回滚。 ## 存在的问题 @@ -40,7 +40,7 @@ Two-phase Commit(2PC)。 1. 提议者(Proposer):提议一个值; 2. 接受者(Acceptor):对每个提议进行投票; -3. 告知者(Learner):被告知投票的结果,不参与投票的过程。 +3. 告知者(Learner):被告知投票的结果,不参与投票过程。

@@ -48,49 +48,51 @@ Two-phase Commit(2PC)。 规定一个提议包含两个字段:[n, v],其中 n 为序号(具有唯一性),v 为提议值。 -下图演示了两个 Proposer 和三个 Acceptor 的系统中运行该算法的初始过程,每个 Proposer 都会向每个 Acceptor 发送提议请求。 +下图演示了两个 Proposer 和三个 Acceptor 的系统中运行该算法的初始过程,每个 Proposer 都会向所有 Acceptor 发送提议请求。

-当 Acceptor 接收到一个提议请求,包含的提议为 [n1, v1],并且之前还未接收过提议请求,那么发送一个提议响应,设置当前接收的提议为 [n1, v1],并且保证以后不会再接受提议值小于 n1 的提议。 +当 Acceptor 接收到一个提议请求,包含的提议为 [n1, v1],并且之前还未接收过提议请求,那么发送一个提议响应,设置当前接收到的提议为 [n1, v1],并且保证以后不会再接受序号小于 n1 的提议。 -如下图,Acceptor X 在收到 [n=2, v=8] 的提议请求时,由于之前没有接收过提议,因此就发送一个 [no previous] 的提议响应,并且设置当前接收的提议为 [n=2, v=8],并且保证以后不会再接受提议值小于 2 的提议。其它的 Acceptor 类似。 +如下图,Acceptor X 在收到 [n=2, v=8] 的提议请求时,由于之前没有接收过提议,因此就发送一个 [no previous] 的提议响应,并且设置当前接收到的提议为 [n=2, v=8],并且保证以后不会再接受序号小于 2 的提议。其它的 Acceptor 类似。 -

+

-如果 Acceptor 接受到一个提议请求,包含的提议为 [n2, v2],并且之前已经接收过提议 [n1, v1]。如果 n1 > n2,那么就丢弃该提议请求;否则,发送提议响应,该提议响应包含之前已经接收过的提议 [n1, v1],设置当前接收的提议为 [n2, v2],并且保证以后不会再接受提议值小于 n2 的提议。 +如果 Acceptor 接受到一个提议请求,包含的提议为 [n2, v2],并且之前已经接收过提议 [n1, v1]。如果 n1 > n2,那么就丢弃该提议请求;否则,发送提议响应,该提议响应包含之前已经接收过的提议 [n1, v1],设置当前接收到的提议为 [n2, v2],并且保证以后不会再接受序号小于 n2 的提议。 -如下图,Acceptor Z 收到 Proposer A 发来的 [n=2, v=8] 的提议请求,由于之前已经接收过 [n=4, v=5] 的提议,并且 n > 2,因此就抛弃该提议请求;Acceptor X 收到 Proposer B 发来的 [n=4, v=5] 的提议请求,因为之前接收的提议为 [n=2, v=8],并且 2 <= 4,因此就发送 [n=2, v=8] 的提议响应,设置当前接收的提议为 [n=4, v=5],并且保证以后不会再接受提议值小于 4 的提议。Acceptor Y 类似。 +如下图,Acceptor Z 收到 Proposer A 发来的 [n=2, v=8] 的提议请求,由于之前已经接收过 [n=4, v=5] 的提议,并且 n > 2,因此就抛弃该提议请求;Acceptor X 收到 Proposer B 发来的 [n=4, v=5] 的提议请求,因为之前接收到的提议为 [n=2, v=8],并且 2 <= 4,因此就发送 [n=2, v=8] 的提议响应,设置当前接收到的提议为 [n=4, v=5],并且保证以后不会再接受序号小于 4 的提议。Acceptor Y 类似。

当一个 Proposer 接收到超过一半 Acceptor 的提议响应时,就可以发送接受请求。 -如下图,Proposer A 接受到两个提议响应之后,就发送 [n=2, v=8] 接受请求。该接受请求会被所有 Acceptor 丢弃,因为此时所有 Acceptor 都保证不接受提议值小于 4 的提议。Proposer B 过后也收到了两个提议响应,因此也开始发送接受请求。需要注意的是,接受请求的 v 需要取它收到的最大 v 值,也就是 8。因此它发送 [n=4, v=8] 的接受请求。 +Proposer A 接受到两个提议响应之后,就发送 [n=2, v=8] 接受请求。该接受请求会被所有 Acceptor 丢弃,因为此时所有 Acceptor 都保证不接受序号小于 4 的提议。 + +Proposer B 过后也收到了两个提议响应,因此也开始发送接受请求。需要注意的是,接受请求的 v 需要取它收到的最大 v 值,也就是 8。因此它发送 [n=4, v=8] 的接受请求。

-Acceptor 接收到接受请求时,如果提议号大于等于该 Acceptor 承诺的最小提议号,那么就发送通知给所有的 Learner。当 Learner 发现有大多数的 Acceptor 接收了某个提议,那么该提议的提议值就被 Paxos 选择出来。 +Acceptor 接收到接受请求时,如果序号大于等于该 Acceptor 承诺的最小序号,那么就发送通知给所有的 Learner。当 Learner 发现有大多数的 Acceptor 接收了某个提议,那么该提议的提议值就被 Paxos 选择出来。 -

+

## 约束条件 ### 1. 正确性 -只有一个提议值会生效。 +指只有一个提议值会生效。 因为 Paxos 协议要求每个生效的提议被多数 Acceptor 接收,并且 Acceptor 不会接受两个不同的提议,因此可以保证正确性。 ### 2. 可终止性 -最后总会有一个提议生效。 +指最后总会有一个提议生效。 Paxos 协议能够让 Proposer 发送的提议朝着能被大多数 Acceptor 接受的那个提议靠拢,因此能够保证可终止性。 # 三、Raft 协议 -Raft 和 Poxas 类似,但是更容易理解,也更容易实现。 +Raft 和 Paxos 类似,但是更容易理解,也更容易实现。 Raft 主要是用来竞选主节点。 diff --git a/notes/代码可读性.md b/notes/代码可读性.md index 5d40b41b..744b8bab 100644 --- a/notes/代码可读性.md +++ b/notes/代码可读性.md @@ -12,6 +12,7 @@ * [十一、一次只做一件事](#十一一次只做一件事) * [十二、用自然语言表述代码](#十二用自然语言表述代码) * [十三、减少代码量](#十三减少代码量) +* [参考资料](#参考资料) @@ -340,3 +341,7 @@ public int findClostElement(int[] arr) { 不要过度设计,编码过程会有很多变化,过度设计的内容到最后往往是无用的。 多用标准库实现。 + +# 参考资料 + +- Dustin, Boswell, Trevor, 等. 编写可读代码的艺术 [M]. 机械工业出版社, 2012. diff --git a/notes/分布式基础.md b/notes/分布式基础.md index 2b1a696d..ba97c7ee 100644 --- a/notes/分布式基础.md +++ b/notes/分布式基础.md @@ -77,11 +77,11 @@ 1. 强一致性:新数据写入之后,在任何数据副本上都能读取到最新值; 2. 弱一致性:新数据写入之后,不能保证在数据副本上能读取到最新值; -3. 最终一致性:新数据写入之后,只能保证过一了一个时间窗口才能读取到最新值; +3. 最终一致性:新数据写入之后,只能保证过了一个时间窗口后才能在数据副本上读取到最新值; ### 4. 可扩展性 -指系统通过扩展集群服务器规模来提高性能的能力。理想的分布式系统需要实现“线性可扩展”,即随着集群规模的增加,系统的整体性能也会线程增加。 +指系统通过扩展集群服务器规模来提高性能的能力。理想的分布式系统需要实现“线性可扩展”,即随着集群规模的增加,系统的整体性能也会线性增加。 # 二、数据分布 @@ -93,7 +93,7 @@ 传统的哈希分布算法存在一个问题:当节点数量变化时,也就是 N 值变化,那么几乎所有的数据都需要重新分布,将导致大量的数据迁移。 -#### 一致性哈希 +**一致性哈希** Distributed Hash Table(DHT):对于哈希空间 0\~2n,将该哈希空间看成一个哈希环,将每个节点都配置到哈希环上。每个数据对象通过哈希取模得到哈希值之后,存放到哈希环中顺时针方向第一个大于等于该哈希值的节点上。 @@ -109,7 +109,7 @@ Distributed Hash Table(DHT):对于哈希空间 0\~2n,将该 顺序分布的数据划分为多个连续的部分,按一定策略分布到不同节点上。例如下图中,User 表的主键范围为 1 \~ 7000,使用顺序分布可以将其划分成多个子表,对应的主键范围为 1 \~ 1000,1001 \~ 2000,...,6001 \~ 7000。 -其中 Meta 表是为了支持更大的集群规模,它将原来的一层索引结分成两层,使用 Meta 表来维护 User 子表所在的节点,从而减轻 Root 节点的负担。 +引入 Meta 表是为了支持更大的集群规模,它将原来的一层索引结分成两层,Meta 维护着 User 子表所在的节点,从而减轻 Root 节点的负担。

@@ -153,13 +153,15 @@ Distributed Hash Table(DHT):对于哈希空间 0\~2n,将该 在设计分布式系统时,需要根据实际需求弱化某一要求。因此就有了下图中的三种设计:CA、CP 和 AP。 -

+

需要注意的是,分区容忍性必不可少,因为需要总是假设网络是不可靠的。因此实际上设计分布式系统需要在一致性和可用性之间做权衡。 # 六、BASE -BASE 是 Basically Available(基本可用)、Soft state(软状态)和 Eventually consistent(最终一致性)三个短语的缩写。BASE 理论是对 CAP 中一致性和可用性权衡的结果,是基于 CAP 定理逐步演化而来的。BASE 理论的核心思想是:即使无法做到强一致性,但每个应用都可以根据自身业务特点,采用适当的方式来使系统达到最终一致性。 +BASE 是 Basically Available(基本可用)、Soft State(软状态)和 Eventually Consistent(最终一致性)三个短语的缩写。BASE 理论是对 CAP 中一致性和可用性权衡的结果,是基于 CAP 定理逐步演化而来的。BASE 理论的核心思想是:即使无法做到强一致性,但每个应用都可以根据自身业务特点,采用适当的方式来使系统达到最终一致性。 + +

## 基本可用 diff --git a/notes/分布式问题分析.md b/notes/分布式问题分析.md index 228d3fb6..9d4ff810 100644 --- a/notes/分布式问题分析.md +++ b/notes/分布式问题分析.md @@ -11,11 +11,10 @@ * [使用场景](#使用场景) * [实现方式](#实现方式) * [五、分布式 Session](#五分布式-session) - * [1. 粘性 Session](#1-粘性-session) - * [2. 服务器 Session 复制](#2-服务器-session-复制) - * [3. Session 共享机制](#3-session-共享机制) - * [4. Session 持久化到数据库](#4-session-持久化到数据库) - * [5. Terracotta 实现 Session 复制](#5-terracotta-实现-session-复制) + * [1. Sticky Sessions](#1-sticky-sessions) + * [2. Session Replication](#2-session-replication) + * [3. Persistent DataStore](#3-persistent-datastore) + * [4. In-Memory DataStore](#4-in-memory-datastore) * [六、分库与分表带来的分布式困境与应对之策](#六分库与分表带来的分布式困境与应对之策) * [事务问题](#事务问题) * [查询问题](#查询问题) @@ -28,9 +27,9 @@ 分布式主要是为了提供可扩展性以及高可用性,业务中使用分布式的场景主要有分布式存储以及分布式计算。 -分布式存储中可以将数据分片到多个节点上,不仅可以提高性能(可扩展性),同时也可以使用多个节点对同一份数据进行备份。 +分布式存储中可以将数据分片到多个节点上,不仅可以提高性能(可扩展性),同时也可以使用多个节点对同一份数据进行备份(高可用性)。 -至于分布式计算,就是将一个大的计算任务分解成小任务分配到多台节点上去执行,再汇总每个小任务的执行结果得到最终结果。MapReduce 是分布式计算的最好例子。 +至于分布式计算,就是将一个大的计算任务分解成小任务分配到多个节点上去执行,再汇总每个小任务的执行结果得到最终结果。MapReduce 是分布式计算最好的例子。 # 二、分布式事务 @@ -43,46 +42,48 @@ ## 应用场景 -- 下单:减少库存、更新订单状态。库存和订单不在不同一个数据库,因此涉及分布式事务。 -- 支付:买家账户扣款、卖家账户入账。买家和卖家账户信息不在同一个数据库,因此涉及分布式事务。 +- 下单:减少库存、更新订单状态。库存和订单如果不在同一个数据库,就涉及分布式事务。 +- 支付:买家账户扣款、卖家账户入账。买家和卖家账户信息如果不在同一个数据库,就涉及分布式事务。 ## 解决方案 ### 1. 两阶段提交协议 -[两阶段提交](https://github.com/CyC2018/Interview-Notebook/blob/master/notes/%E4%B8%80%E8%87%B4%E6%80%A7%E5%8D%8F%E8%AE%AE.md#%E4%B8%A4%E9%98%B6%E6%AE%B5%E6%8F%90%E4%BA%A4%E5%8D%8F%E8%AE%AE) +> [两阶段提交](https://github.com/CyC2018/Interview-Notebook/blob/master/notes/%E4%B8%80%E8%87%B4%E6%80%A7%E5%8D%8F%E8%AE%AE.md#%E4%B8%A4%E9%98%B6%E6%AE%B5%E6%8F%90%E4%BA%A4%E5%8D%8F%E8%AE%AE) -两阶段提交协议可以很好得解决分布式事务问题,它可以使用 XA 来实现,XA 它包含两个部分:事务管理器和本地资源管理器。其中本地资源管理器往往由数据库实现,比如 Oracle、DB2 这些商业数据库都实现了 XA 接口;而事务管理器作为全局的协调者,负责各个本地资源的提交和回滚。 +两阶段提交协议可以很好地解决分布式事务问题。它可以使用 XA 来实现,XA 包含两个部分:事务管理器和本地资源管理器。其中本地资源管理器往往由数据库实现,比如 Oracle、DB2 这些商业数据库都实现了 XA 接口;而事务管理器作为全局的协调者,负责各个本地资源的提交和回滚。 ### 2. 消息中间件 消息中间件也可称作消息系统 (MQ),它本质上是一个暂存转发消息的一个中间件。在分布式应用当中,我们可以把一个业务操作转换成一个消息,比如支付宝的余额转入余额宝操作,支付宝系统执行减少余额操作之后向消息系统发送一个消息,余额宝系统订阅这条消息然后进行增加余额宝操作。 -**(一)消息处理模型** +#### 2.1 消息处理模型 - **点对点**
+(一)点对点 -

+

- **发布/订阅**
+(二)发布/订阅

-**(二)消息的可靠性** -消息的发送端的可靠性:发送端完成操作后一定能将消息成功发送到消息系统。 +#### 2.2 消息的可靠性 -消息的接收端的可靠性:接收端仅且能够从消息中间件成功消费一次消息。 +(一)发送端的可靠性 - **发送端的可靠性**
+发送端完成操作后一定能将消息成功发送到消息系统。 -在本地数据建一张消息表,将消息数据与业务数据保存在同一数据库实例里,这样就可以利用本地数据库的事务机制。事务提交成功后,将消息表中的消息转移到消息中间件,若转移消息成功则删除消息表中的数据,否则继续重传。 +实现方法:在本地数据建一张消息表,将消息数据与业务数据保存在同一数据库实例里,这样就可以利用本地数据库的事务机制。事务提交成功后,将消息表中的消息转移到消息中间件,若转移消息成功则删除消息表中的数据,否则继续重传。 - **接收端的可靠性**
+(二)接收端的可靠性 -保证接收端处理消息的业务逻辑具有幂等性:只要具有幂等性,那么消费多少次消息,最后处理的结果都是一样的。 +接收端仅且能够从消息中间件成功消费一次消息。 -保证消息具有唯一编号,并使用一张日志表来记录已经消费的消息编号。 +实现方法: + +- 保证接收端处理消息的业务逻辑具有幂等性:只要具有幂等性,那么消费多少次消息,最后处理的结果都是一样的。 +- 保证消息具有唯一编号,并使用一张日志表来记录已经消费的消息编号。 # 三、负载均衡的算法与实现 @@ -94,7 +95,7 @@

-该算法比较适合每个服务器的性能差不多的场景,如果有性能存在差异的情况下,那么性能较差的服务器可能无法承担多大的负载。下图中,服务器 2 的性能比服务器 1 差,那么服务器 2 可能无法承担多大的负载。 +该算法比较适合每个服务器的性能差不多的场景,如果有性能存在差异的情况下,那么性能较差的服务器可能无法承担多大的负载(下图的 Server 2)。

@@ -128,45 +129,57 @@ ## 实现 -### 1. DNS 解析 - -使用 DNS 作为负载均衡器,根据负载情况返回不同服务器的 IP 地址。大型网站基本使用了这种方式最为第一级负载均衡手段,然后在内部使用其它方式做第二级负载均衡。 - -

- -### 2. 修改 MAC 地址 - -使用 LVS(Linux Virtual Server)这种链路层负载均衡器,根据负载情况修改请求的 MAC 地址。 - -

- -### 3. 修改 IP 地址 - -在网络层修改请求的目的 IP 地址。 - -

- -### 4. HTTP 重定向 +### 1. HTTP 重定向 HTTP 重定向负载均衡服务器收到 HTTP 请求之后会返回服务器的地址,并将该地址写入 HTTP 重定向响应中返回给浏览器,浏览器收到后需要再次发送请求。 -

+缺点: -### 5. 反向代理 +- 用户访问的延迟会增加; +- 如果负载均衡器宕机,就无法访问该站点。 -正向代理:发生在客户端,是由用户主动发起的。比如翻墙,客户端通过主动访问代理服务器,让代理服务器获得需要的外网数据,然后转发回客户端。 +

-反向代理:发生在服务器端,用户不知道代理的存在。 +### 2. DNS 重定向 -

+使用 DNS 作为负载均衡器,根据负载情况返回不同服务器的 IP 地址。大型网站基本使用了这种方式做为第一级负载均衡手段,然后在内部使用其它方式做第二级负载均衡。 + +缺点: + +- DNS 查找表可能会被客户端缓存起来,那么之后的所有请求都会被重定向到同一个服务器。 + +

+ +### 3. 修改 MAC 地址 + +使用 LVS(Linux Virtual Server)这种链路层负载均衡器,根据负载情况修改请求的 MAC 地址。 + +

+ +### 4. 修改 IP 地址 + +在网络层修改请求的目的 IP 地址。 + +

+ +### 5. 代理自动配置 + +正向代理与反向代理的区别: + +- 正向代理:发生在客户端,是由用户主动发起的。比如翻墙,客户端通过主动访问代理服务器,让代理服务器获得需要的外网数据,然后转发回客户端。 +- 反向代理:发生在服务器端,用户不知道代理的存在。 + +PAC 服务器是用来判断一个请求是否要经过代理。 + +

# 四、分布式锁 -Java 提供了两种内置的锁的实现,一种是由 JVM 实现的 synchronized 和 JDK 提供的 Lock,当你的应用是单机或者说单进程应用时,可以使用 synchronized 或 Lock 来实现锁。当应用涉及到多机、多进程共同完成时,那么这时候就需要一个全局锁来实现多个进程之间的同步。 +Java 提供了两种内置的锁的实现,一种是由 JVM 实现的 synchronized 和 JDK 提供的 Lock,对于单机单进程应用,可以使用它们来实现锁。当应用涉及到多机、多进程共同完成时,那么这时候就需要一个全局锁来实现多个进程之间的同步。 ## 使用场景 -在服务器端使用分布式部署的情况下,一个服务可能分布在不同的节点撒花姑娘,比如订单服务分布式在节点 A 和节点 B 上。如果多个客户端同时对一个服务进行请求时,就需要使用分布式锁。例如当 APP 端的请求路由到了节点 A,WEB 端被路由到了 B 服务,这时对共享资源进行使用时需要使用分布式锁。 +在服务器端使用分布式部署的情况下,一个服务可能分布在不同的节点上,比如订单服务分布式在节点 A 和节点 B 上。如果多个客户端同时对一个服务进行请求时,就需要使用分布式锁。例如一个服务可以使用 APP 端或者 Web 端进行访问,如果一个用户同时使用 APP 端和 Web 端访问该服务,并且 APP 端的请求路由到了节点 A,WEB 端的请求被路由到了节点 B,这时候就需要使用分布式锁来进行同步。 ## 实现方式 @@ -174,7 +187,9 @@ Java 提供了两种内置的锁的实现,一种是由 JVM 实现的 synchroni **(一)基于 MySQL 锁表** -该实现方式完全依靠数据库唯一索引来实现。当想要获得锁时,就向数据库中插入一条记录,释放锁时就删除这条记录。如果记录具有唯一索引,就不会同时插入同一条记录。这种方式存在以下几个问题: +该实现完全依靠数据库的唯一索引。当想要获得锁时,就向数据库中插入一条记录,释放锁时就删除这条记录。如果记录具有唯一索引,就不会同时插入同一条记录。 + +这种方式存在以下几个问题: 1. 锁没有失效时间,解锁失败会导致死锁,其他线程无法再获得锁。 2. 只能是非阻塞锁,插入失败直接就报错了,无法重试。 @@ -194,7 +209,7 @@ EXPIRE 可以为一个键值对设置一个过期时间,从而避免了死锁 **(二)RedLock 算法** -ReadLock 算法使用了多个 Redis 实例来实现分布式锁,这是为了保证在发生单点故障时还可用。 +RedLock 算法使用了多个 Redis 实例来实现分布式锁,这是为了保证在发生单点故障时还可用。 1. 尝试从 N 个相互独立 Redis 实例获取锁,如果一个实例不可用,应该尽快尝试下一个。 2. 计算获取锁消耗的时间,只有当这个时间小于锁的过期时间,并且从大多数(N/2+1)实例上获取了锁,那么就认为锁获取成功了。 @@ -233,103 +248,41 @@ Zookeeper 提供了一种树形结构级的命名空间,/app1/p_1 节点表示 **(六)羊群效应** -在步骤二,一个节点未获得锁,需要监听监听自己的前一个子节点,这是因为如果监听所有的子节点,那么任意一个子节点状态改变,其它所有子节点都会收到通知,而我们只希望它的下一个子节点收到通知。 +在步骤二,一个节点未获得锁,需要监听监听自己的前一个子节点,这是因为如果监听所有的子节点,那么任意一个子节点状态改变,其它所有子节点都会收到通知(羊群效应),而我们只希望它的后一个子节点收到通知。 # 五、分布式 Session -如果不做任何处理的话,用户将出现频繁登录的现象,比如集群中存在 A、B 两台服务器,用户在第一次访问网站时,Nginx 通过其负载均衡机制将用户请求转发到 A 服务器,这时 A 服务器就会给用户创建一个 Session。当用户第二次发送请求时,Nginx 将其负载均衡到 B 服务器,而这时候 B 服务器并不存在 Session,所以就会将用户踢到登录页面。这将大大降低用户体验度,导致用户的流失,这种情况是项目绝不应该出现的。 +在分布式场景下,一个用户的 Session 如果只存储在一个服务器上,那么当负载均衡器把用户的下一个请求转发到另一个服务器上,该服务器没有用户的 Session,就可能导致用户需要重新进行登录等操作。 -## 1. 粘性 Session +

-### 原理 +## 1. Sticky Sessions -粘性 Session 是指将用户锁定到某一个服务器上,比如上面说的例子,用户第一次请求时,负载均衡器将用户的请求转发到了 A 服务器上,如果负载均衡器设置了粘性 Session 的话,那么用户以后的每次请求都会转发到 A 服务器上,相当于把用户和 A 服务器粘到了一块,这就是粘性 Session 机制。 +需要配置负载均衡器,使得一个用户的所有请求都路由到一个服务器节点上,这样就可以把用户的 Session 存放在该服务器节点中。 -### 优点 +缺点:当服务器节点宕机时,将丢失该服务器节点上的所有 Session。 -简单,不需要对 Session 做任何处理。 +

-### 缺点 +## 2. Session Replication -缺乏容错性,如果当前访问的服务器发生故障,用户被转移到第二个服务器上时,他的 Session 信息都将失效。 +在服务器节点之间进行 Session 同步操作,这样的话用户可以访问任何一个服务器节点。 -### 适用场景 +缺点:需要更好的服务器硬件条件;需要对服务器进行配置。 -- 发生故障对客户产生的影响较小; -- 服务器发生故障是低概率事件。 +

-## 2. 服务器 Session 复制 +## 3. Persistent DataStore -### 原理 +将 Session 信息持久化到一个数据库中。 -任何一个服务器上的 Session 发生改变,该节点会把这个 Session 的所有内容序列化,然后广播给所有其它节点,不管其他服务器需不需要 Session,以此来保证 Session 同步。 +缺点:有可能需要去实现存取 Session 的代码。 -### 优点 +

-可容错,各个服务器间 Session 能够实时响应。 +## 4. In-Memory DataStore -### 缺点 - -会对网络负荷造成一定压力,如果 Session 量大的话可能会造成网络堵塞,拖慢服务器性能。 - -### 实现方式 - -1. 设置 Tomcat 的 server.xml 开启 tomcat 集群功能。 -2. 在应用里增加信息:通知应用当前处于集群环境中,支持分布式,即在 web.xml 中添加<distributable/> 选项。 - -## 3. Session 共享机制 - -使用分布式缓存方案比如 Memcached、Redis,但是要求 Memcached 或 Redis 必须是集群。 - -使用 Session 共享也分两种机制,两种情况如下: - -### 3.1 粘性 Session 共享机制 - -和粘性 Session 一样,一个用户的 Session 会绑定到一个 Tomcat 上。Memcached 只是起到备份作用。 - -

- -### 3.2 非粘性 Session 共享机制 - -#### 原理 - -Tomcat 本身不存储 Session,而是存入 Memcached 中。Memcached 集群构建主从复制架构。 - -

- -#### 优点 - -可容错,Session 实时响应。 - -#### 实现方式 - -用开源的 msm 插件解决 Tomcat 之间的 Session 共享:Memcached_Session_Manager(MSM) - -## 4. Session 持久化到数据库 - -### 原理 - -拿出一个数据库,专门用来存储 Session 信息。保证 Session 的持久化。 - -### 优点 - -服务器出现问题,Session 不会丢失 - -### 缺点 - -如果网站的访问量很大,把 Session 存储到数据库中,会对数据库造成很大压力,还需要增加额外的开销维护数据库。 - -## 5. Terracotta 实现 Session 复制 - -### 原理 - -Terracotta 的基本原理是对于集群间共享的数据,当在一个节点发生变化的时候,Terracotta 只把变化的部分发送给 Terracotta 服务器,然后由服务器把它转发给真正需要这个数据的节点。它是服务器 Session 复制的优化。 - -

- -### 优点 - -这样对网络的压力就非常小,各个节点也不必浪费 CPU 时间和内存进行大量的序列化操作。把这种集群间数据共享的机制应用在 Session 同步上,既避免了对数据库的依赖,又能达到负载均衡和灾难恢复的效果。 +可以使用 Redis 和 Memcached 这种内存型数据库对 Session 进行存储,可以大大提高 Session 的读写效率。内存型数据库同样可以持久化数据到磁盘中来保证数据的安全性。 # 六、分库与分表带来的分布式困境与应对之策 @@ -353,6 +306,9 @@ Terracotta 的基本原理是对于集群间共享的数据,当在一个节点 - [Comparing Load Balancing Algorithms](http://www.jscape.com/blog/load-balancing-algorithms) - [负载均衡算法及手段](https://segmentfault.com/a/1190000004492447) +- [Redirection and Load Balancing](http://slideplayer.com/slide/6599069/#) +- [Session Management using Spring Session with JDBC DataStore](https://sivalabs.in/2018/02/session-management-using-spring-session-jdbc-datastore/) +- [Apache Wicket User Guide - Reference Documentation](https://ci.apache.org/projects/wicket/guide/6.x/) - [集群/分布式环境下 5 种 Session 处理策略](http://blog.csdn.net/u010028869/article/details/50773174?ref=myread) - [浅谈分布式锁](http://www.linkedkeeper.com/detail/blog.action?bid=1023) - [深入理解分布式事务](https://juejin.im/entry/577c6f220a2b5800573492be) diff --git a/notes/剑指 offer 题解.md b/notes/剑指 offer 题解.md index b367d103..cdc4e4c1 100644 --- a/notes/剑指 offer 题解.md +++ b/notes/剑指 offer 题解.md @@ -1,5 +1,4 @@ -* [前言](#前言) * [2. 实现 Singleton](#2-实现-singleton) * [3. 数组中重复的数字](#3-数组中重复的数字) * [4. 二维数组中的查找](#4-二维数组中的查找) @@ -30,8 +29,7 @@ * [25. 合并两个排序的链表](#25-合并两个排序的链表) * [26. 树的子结构](#26-树的子结构) * [27. 二叉树的镜像](#27-二叉树的镜像) -* [28.1 对称的二叉树](#281-对称的二叉树) -* [28.2 平衡二叉树](#282-平衡二叉树) +* [28 对称的二叉树](#28-对称的二叉树) * [29. 顺时针打印矩阵](#29-顺时针打印矩阵) * [30. 包含 min 函数的栈](#30-包含-min-函数的栈) * [31. 栈的压入、弹出序列](#31-栈的压入弹出序列) @@ -60,8 +58,9 @@ * [51. 数组中的逆序对](#51-数组中的逆序对) * [52. 两个链表的第一个公共结点](#52-两个链表的第一个公共结点) * [53 数字在排序数组中出现的次数](#53-数字在排序数组中出现的次数) -* [54. 二叉搜索树的第 k 个结点](#54-二叉搜索树的第-k-个结点) -* [55 二叉树的深度](#55-二叉树的深度) +* [54. 二叉搜索树的第 K 个结点](#54-二叉搜索树的第-k-个结点) +* [55.1 二叉树的深度](#551-二叉树的深度) +* [55.2 平衡二叉树](#552-平衡二叉树) * [56. 数组中只出现一次的数字](#56-数组中只出现一次的数字) * [57.1 和为 S 的两个数字](#571-和为-s-的两个数字) * [57.2 和为 S 的连续正数序列](#572-和为-s-的连续正数序列) @@ -77,29 +76,13 @@ * [66. 构建乘积数组](#66-构建乘积数组) * [67. 把字符串转换成整数](#67-把字符串转换成整数) * [68. 树中两个节点的最低公共祖先](#68-树中两个节点的最低公共祖先) +* [参考文献](#参考文献) -# 前言 - -## 变量命名约定 - -- nums 表示数字数组,array 表示通用数组,matrix 表示矩阵; -- n 表示数组长度、字符串长度、树节点个数,以及其它具有一维性质的数据结构的元素个数; -- m, n 表示矩阵的行数和列数; -- first, last 表示闭区间,在需要作为函数参数时使用:[first, last]; -- l, h 也表示闭区间,在只作为局部变量时使用:[l, h]; -- begin, end 表示左闭右开区间:[begin, end); -- ret 表示结果相关的变量; -- dp 表示动态规划保存子问题的数组; - -## 复杂度简写说明 - -O(nlogn) + O(n2),第一个指时间复杂度,第二个指空间复杂度。 - # 2. 实现 Singleton -> [单例模式](https://github.com/CyC2018/Interview-Notebook/blob/master/notes/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F.md#%E5%85%AB%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F) +> [单例模式](https://github.com/CyC2018/Interview-Notebook/blob/master/notes/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F.md) # 3. 数组中重复的数字 @@ -111,7 +94,7 @@ O(nlogn) + O(n2),第一个指时间复杂度,第二个 这种数组元素在 [0, n-1] 范围内的问题,可以将值为 i 的元素放到第 i 个位置上。 -以 (2, 3, 1, 0, 2, 5) 为例,以下代码的运行过程为: +以 (2, 3, 1, 0, 2, 5) 为例: ```html position-0 : (2,3,1,0,2,5) // 2 <-> 1 @@ -124,7 +107,7 @@ position-2 : (0,1,1,3,2,5) // nums[i] == nums[nums[i]], exit 遍历到位置 2 时,该位置上的数为 1,但是第 1 个位置上已经有一个 1 的值了,因此可以知道 1 重复。 -复杂度:O(n) + O(1) +复杂度:O(N) + O(1) ```java public boolean duplicate(int[] nums, int length, int[] duplication) { @@ -170,7 +153,7 @@ Given target = 20, return false. 从右上角开始查找。因为矩阵中的一个数,它左边的数都比它小,下边的数都比它大。因此,从右上角开始查找,就可以根据 target 和当前元素的大小关系来缩小查找区间。 -复杂度:O(m + n) + O(1) +复杂度:O(M+N) + O(1) ```java public boolean Find(int target, int[][] matrix) { @@ -200,9 +183,7 @@ public boolean Find(int target, int[][] matrix) { 从后向前遍是为了在改变 P2 所指向的内容时,不会影响到 P1 遍历原来字符串的内容。 -复杂度:O(n) + O(1) - -

+复杂度:O(N) + O(1) ```java public String replaceSpace(StringBuffer str) { @@ -234,6 +215,8 @@ public String replaceSpace(StringBuffer str) { 输入链表的第一个节点,从尾到头反过来打印出每个结点的值。 +

+ ## 解题思路 ### 使用栈 @@ -314,20 +297,12 @@ public ArrayList printListFromTailToHead(ListNode listNode) { 根据二叉树的前序遍历和中序遍历的结果,重建出该二叉树。 ```html -For example, given - preorder = [3,9,20,15,7] inorder = [9,3,15,20,7] - -Return the following binary tree: - - 3 - / \ - 9 20 - / \ - 15 7 ``` +

+ ## 解题思路 前序遍历的第一个值为根节点的值,使用这个值将中序遍历结果分成两部分,左部分为树的左子树中序遍历结果,右部分为树的右子树中序遍历的结果。 @@ -364,11 +339,11 @@ private TreeNode reConstructBinaryTree(int[] pre, int preL, int preR, int[] in, ① 如果一个节点有右子树不为空,那么该节点的下一个节点是右子树的最左节点; -

+

② 否则,向上找第一个左链接指向的树包含该节点的祖先节点。 -

+

```java public class TreeLinkNode { @@ -406,6 +381,8 @@ public TreeLinkNode GetNext(TreeLinkNode pNode) { in 栈用来处理入栈(push)操作,out 栈用来处理出栈(pop)操作。一个元素进入 in 栈之后,出栈的顺序被反转。当元素要出栈时,需要先进入 out 栈,此时元素出栈顺序再一次被反转,因此出栈顺序就和最开始入栈顺序是相同的,此时先进入的元素先退出,这就是队列的顺序。 +

+ ```java Stack in = new Stack(); Stack out = new Stack(); @@ -436,7 +413,7 @@ public int pop() { 如果使用递归求解,那么会重复计算一些子问题。例如,求 f(10) 需要计算 f(9) 和 f(8),计算 f(9) 需要计算 f(8) 和 f(7),可以看到 f(8) 被重复计算了。 -

+

递归方法是将一个问题划分成多个子问题求解,动态规划也是如此,但是动态规划会把子问题的解缓存起来,避免重复求解子问题。 @@ -529,7 +506,7 @@ public int RectCover(int n) { ### 分治 -复杂度:O(logn) + O(1),其实空间复杂度不止 O(1),因为分治使用了递归栈,用到了额外的空间,如果对空间有要求就不能用这种方法。 +复杂度:O(logN) + O(1),其实空间复杂度不止 O(1),因为分治使用了递归栈,用到了额外的空间,如果对空间有要求就不能用这种方法。 ```java public int minNumberInRotateArray(int[] nums) { @@ -544,9 +521,9 @@ private int minNumberInRotateArray(int[] nums, int first, int last) { } ``` -### 双指针 +### 二分查找 -复杂度:O(logn) + O(1) +复杂度:O(logN) + O(1) ```java public int minNumberInRotateArray(int[] nums) { @@ -556,7 +533,7 @@ public int minNumberInRotateArray(int[] nums) { if (h - l == 1) return nums[h]; int mid = l + (h - l) / 2; if (nums[mid] >= nums[l]) l = mid; - else if (nums[mid] <= nums[h]) h = mid; + else h = mid; } return nums[l]; } @@ -566,7 +543,11 @@ public int minNumberInRotateArray(int[] nums) { ## 题目描述 -请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。例如 a b c e s f c s a d e e 矩阵中包含一条字符串 "bcced" 的路径,但是矩阵中不包含 "abcb" 路径,因为字符串的第一个字符 b 占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。 +请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。 + +例如下面的矩阵包含了一条 bfce 路径。 + +

## 解题思路 @@ -694,7 +675,7 @@ public int maxProductAfterCutting(int n) { ### 贪心解法 -尽可能多得剪长度为 3 的绳子,并且不允许有长度为 1 的绳子出现,如果出现了,就从已经切好长度为 3 的绳子中拿出一段与长度为 1 的绳子重新组合,把它们切成两段长度为 2 的绳子。 +尽可能多剪长度为 3 的绳子,并且不允许有长度为 1 的绳子出现,如果出现了,就从已经切好长度为 3 的绳子中拿出一段与长度为 1 的绳子重新组合,把它们切成两段长度为 2 的绳子。 证明:当 n >= 5 时,3(n - 3) - 2(n - 2) = n - 5 >= 0。因此把长度大于 5 的绳子切成两段,令其中一段长度为 3 可以使得两段的乘积最大。 @@ -726,7 +707,7 @@ public int NumberOf1(int n) { ### n&(n-1) -O(logM) 时间复杂度解法,其中 m 表示 1 的个数。 +O(logM) 时间复杂度解法,其中 M 表示 1 的个数。 该位运算是去除 n 的位级表示中最低的那一位。 @@ -772,7 +753,7 @@ public double Power(double base, int exponent) { } double pow = Power(base * base, exponent / 2); if (exponent % 2 != 0) pow = pow * base; - return isNegative ? 1 / pow : pow; + return isNegative ? (1 / pow) : pow; } ``` @@ -805,6 +786,13 @@ private void print1ToMaxOfNDigits(char[] number, int digit) { print1ToMaxOfNDigits(number, digit + 1); } } + +private void printNumber(char[] number) { + int index = 0; + while (index < number.length && number[index] == '0') index++; + while (index < number.length) System.out.print(number[index++]); + System.out.println(); +} ``` # 18.1 在 O(1) 时间内删除链表节点 @@ -813,13 +801,13 @@ private void print1ToMaxOfNDigits(char[] number, int digit) { ① 如果该节点不是尾节点,那么可以直接将下一个节点的值赋给该节点,令该节点指向下下个节点,然后删除下一个节点,时间复杂度为 O(1)。 -

+

② 否则,就需要先遍历链表,找到节点的前一个节点,然后让前一个节点指向 null,时间复杂度为 O(N)。 -

+

-综上,如果进行 N 次操作,那么大约需要操作节点的次数为 N-1+N=2N-1,其中 N-1 表示 N-1 个不是尾节点的每个节点以 O(1) 的时间复杂度操作节点的总次数,N 表示 1 个为节点以 O(n) 的时间复杂度操作节点的总次数。(2N-1)/N \~ 2,因此该算法的平均时间复杂度为 O(1)。 +综上,如果进行 N 次操作,那么大约需要操作节点的次数为 N-1+N=2N-1,其中 N-1 表示 N-1 个不是尾节点的每个节点以 O(1) 的时间复杂度操作节点的总次数,N 表示 1 个尾节点以 O(N) 的时间复杂度操作节点的总次数。(2N-1)/N \~ 2,因此该算法的平均时间复杂度为 O(1)。 ```java public ListNode deleteNode(ListNode head, ListNode tobeDelete) { @@ -842,10 +830,7 @@ public ListNode deleteNode(ListNode head, ListNode tobeDelete) { ## 题目描述 -```html -Input : 1->2->3->3->4->4->5 -Output : 1->2->5 -``` +

## 解题描述 @@ -871,17 +856,17 @@ public ListNode deleteDuplication(ListNode pHead) { ## 解题思路 -应该注意到,'.' 是用来代替一个任意字符,而 '\*' 是用来重复前面的字符。这两个的作用不同,不能把 '.' 的作用和 '\*' 进行类比,从而它当成重复前面字符一次。 +应该注意到,'.' 是用来当做一个任意字符,而 '\*' 是用来重复前面的字符。这两个的作用不同,不能把 '.' 的作用和 '\*' 进行类比,从而把它当成重复前面字符一次。 ```html p.charAt(j) == s.charAt(i) : dp[i][j] = dp[i-1][j-1]; p.charAt(j) == '.' : dp[i][j] = dp[i-1][j-1]; p.charAt(j) == '*' : - p.charAt(j-1) != s.charAt(i) : dp[i][j] = dp[i][j-2] // in this case, a* only counts as empty + p.charAt(j-1) != s.charAt(i) : dp[i][j] = dp[i][j-2] //a* only counts as empty p.charAt(j-1) == s.charAt(i) or p.charAt(i-1) == '.': - dp[i][j] = dp[i-1][j] // in this case, a* counts as multiple a - or dp[i][j] = dp[i][j-1] // in this case, a* counts as single a - or dp[i][j] = dp[i][j-2] // in this case, a* counts as empty + dp[i][j] = dp[i-1][j] // a* counts as multiple a + or dp[i][j] = dp[i][j-1] // a* counts as single a + or dp[i][j] = dp[i][j-2] // a* counts as empty ``` ```java @@ -933,7 +918,7 @@ public boolean isNumeric(char[] str) { ## 解题思路 -复杂度:O(n2) + O(1) +复杂度:O(N2) + O(1) ```java public void reOrderArray(int[] nums) { @@ -953,7 +938,7 @@ public void reOrderArray(int[] nums) { } ``` -复杂度:O(n) + O(n) +复杂度:O(N) + O(N) ```java public void reOrderArray(int[] nums) { @@ -976,7 +961,7 @@ public void reOrderArray(int[] nums) { ## 解题思路 -

+

```java public ListNode FindKthToTail(ListNode head, int k) { @@ -1001,7 +986,7 @@ public ListNode FindKthToTail(ListNode head, int k) { 在相遇点,slow 要到环的入口点还需要移动 z 个节点,如果让 fast 重新从头开始移动,并且速度变为每次移动一个节点,那么它到环入口点还需要移动 x 个节点。在上面已经推导出 x=z,因此 fast 和 slow 将在环入口点相遇。 -

+

```java public ListNode EntryNodeOfLoop(ListNode pHead) { @@ -1059,7 +1044,7 @@ public ListNode ReverseList(ListNode head) { ## 题目描述 -

+

## 解题思路 @@ -1105,7 +1090,7 @@ public ListNode Merge(ListNode list1, ListNode list2) { ## 题目描述 -

+

## 解题思路 @@ -1128,7 +1113,7 @@ private boolean isSubtree(TreeNode root1, TreeNode root2) { ## 题目描述 -

+

## 解题思路 @@ -1147,17 +1132,11 @@ private void swap(TreeNode root) { } ``` -# 28.1 对称的二叉树 +# 28 对称的二叉树 ## 题目描述 -```html - 1 - / \ - 2 2 - / \ / \ -3 4 4 3 -``` +

## 解题思路 @@ -1175,48 +1154,13 @@ boolean isSymmetrical(TreeNode t1, TreeNode t2) { } ``` -# 28.2 平衡二叉树 - -## 题目描述 - -```html - 3 - / \ - 9 20 - / \ - 15 7 -``` - -平衡二叉树左右子树高度差不超过 1。 - -## 解题思路 - -```java -private boolean isBalanced = true; - -public boolean IsBalanced_Solution(TreeNode root) { - height(root); - return isBalanced; -} - -private int height(TreeNode root) { - if (root == null) return 0; - int left = height(root.left); - int right = height(root.right); - if (Math.abs(left - right) > 1) isBalanced = false; - return 1 + Math.max(left, right); -} -``` - # 29. 顺时针打印矩阵 ## 题目描述 下图的矩阵顺时针打印结果为:1, 2, 3, 4, 8, 12, 16, 15, 14, 13, 9, 5, 6, 7, 11, 10 -

- -

+

## 解题思路 @@ -1277,6 +1221,8 @@ public int min() { ## 解题思路 +使用一个栈来模拟压入弹出操作。 + ```java public boolean IsPopOrder(int[] pushA, int[] popA) { int n = pushA.length; @@ -1298,9 +1244,9 @@ public boolean IsPopOrder(int[] pushA, int[] popA) { 从上往下打印出二叉树的每个节点,同层节点从左至右打印。 -例如,以下二叉树层次遍历的结果为 8, 6, 10, 5, 7, 9, 11 +例如,以下二叉树层次遍历的结果为:1,2,3,4,5,6,7 -

+

## 解题思路 @@ -1394,9 +1340,9 @@ public ArrayList> Print(TreeNode pRoot) { 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。 -例如,下图中后序遍历序列 5, 7, 6, 9, 11, 10, 8 所对应的二叉搜索树。 +例如,下图是后序遍历序列 3,1,2 所对应的二叉搜索树。 -

+

## 解题思路 @@ -1426,7 +1372,7 @@ private boolean verify(int[] sequence, int first, int last) { 下图的二叉树有两条和为 22 的路径:10, 5, 7 和 10, 12 -

+

## 解题思路 @@ -1458,21 +1404,21 @@ private void dfs(TreeNode node, int target, ArrayList path) { 输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的 head。 -

+

## 解题思路 第一步,在每个节点的后面插入复制的节点。 -

+

第二步,对复制节点的 random 链接进行赋值。 -

+

第三步,拆分。 -

+

```java public RandomListNode Clone(RandomListNode pHead) { @@ -1514,7 +1460,7 @@ public RandomListNode Clone(RandomListNode pHead) { 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。 -

+

## 解题思路 @@ -1554,7 +1500,7 @@ public class Solution { public String Serialize(TreeNode root) { if (root == null) return "#"; - return root.val + " " + Serialize(root.left) + " " + Serialize(root.right); + return root.val + " " + Serialize(root.left) + " " + Serialize(root.right); } public TreeNode Deserialize(String str) { @@ -1617,7 +1563,7 @@ private void backtracking(char[] chars, boolean[] hasUsed, StringBuffer s) { ## 解题思路 -多数投票问题,可以利用 Boyer-Moore Majority Vote Algorithm 来解决这个问题,使得时间复杂度为 O(n)。 +多数投票问题,可以利用 Boyer-Moore Majority Vote Algorithm 来解决这个问题,使得时间复杂度为 O(N)。 使用 cnt 来统计一个元素出现的次数,当遍历到的元素和统计元素不相等时,令 cnt--。如果前面查找了 i 个元素,且 cnt == 0 ,说明前 i 个元素没有 majority,或者有 majority,但是出现的次数少于 i / 2 ,因为如果多于 i / 2 的话 cnt 就一定不会为 0 。此时剩下的 n - i 个元素中,majority 的数目依然多于 (n - i) / 2,因此继续查找就能找出 majority。 @@ -2027,6 +1973,8 @@ public int GetUglyNumber_Solution(int N) { ## 解题思路 +最直观的解法是使用 HashMap 对出现次数进行统计,但是考虑到要统计的字符范围有限,因此可以使用整型数组代替 HashMap。 + ```java public int FirstNotRepeatingChar(String str) { int[] cnts = new int[256]; @@ -2036,6 +1984,24 @@ public int FirstNotRepeatingChar(String str) { } ``` +以上实现的空间复杂度还不是最优的。考虑到只需要找到只出现一次的字符,那么我们只需要统计的次数信息只有 0,1,更大,那么使用两个比特位就能存储这些信息。 + +```java +public int FirstNotRepeatingChar(String str) { + BitSet bs1 = new BitSet(256); + BitSet bs2 = new BitSet(256); + for (char c : str.toCharArray()) { + if (!bs1.get(c) && !bs2.get(c)) bs1.set(c); // 0 0 + else if (bs1.get(c) && !bs2.get(c)) bs2.set(c); // 0 1 + } + for (int i = 0; i < str.length(); i++) { + char c = str.charAt(i); + if (bs1.get(c) && !bs2.get(c)) return i; + } + return -1; +} +``` + # 51. 数组中的逆序对 ## 题目描述 @@ -2070,7 +2036,7 @@ private void merge(int[] nums, int first, int mid, int last) { else if (nums[i] < nums[j]) tmp[k] = nums[i++]; else { tmp[k] = nums[j++]; - this.cnt += mid - i + 1; // a[i] > a[j],说明 a[i...mid] 都大于 a[j] + this.cnt += mid - i + 1; // nums[i] > nums[j],说明 nums[i...mid] 都大于 nums[j] } k++; } @@ -2084,19 +2050,13 @@ private void merge(int[] nums, int first, int mid, int last) { ## 题目描述 -```html -A: a1 → a2 - ↘ - c1 → c2 → c3 - ↗ -B: b1 → b2 → b3 -``` +

## 解题思路 设 A 的长度为 a + c,B 的长度为 b + c,其中 c 为尾部公共部分长度,可知 a + c + b = b + c + a。 -当访问 A 链表的指针访问到链表尾部时,令它从链表 B 的头部开始访问链表 B;同样地,当访问 B 链表的指针访问到链表尾部时,令它从链表 A 的头部开始访问链表 A。这样就能控制访问 A 和 B 两个链表的指针能同时访问到交点。 +当访问 A 链表的指针访问到链表尾部时,令它从链表 B 的头部重新开始访问链表 B;同样地,当访问 B 链表的指针访问到链表尾部时,令它从链表 A 的头部重新开始访问链表 A。这样就能控制访问 A 和 B 两个链表的指针能同时访问到交点。 ```java public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) { @@ -2123,7 +2083,10 @@ Output: ## 解题思路 -可以用二分查找找出数字在数组的最左端和最右端。 +可以用二分查找找出数字在数组的最左端和最右端,找最左端和最右端在方法实现上的区别主要在于对 nums[m] == K 的处理: + +- 找最左端令 h = m - 1 +- 找最右端令 l = m + 1 ```java public int GetNumberOfK(int[] nums, int K) { @@ -2155,7 +2118,7 @@ private int getLastK(int[] nums, int K) { } ``` -# 54. 二叉搜索树的第 k 个结点 +# 54. 二叉搜索树的第 K 个结点 ## 解题思路 @@ -2180,7 +2143,15 @@ private void inOrder(TreeNode root, int k) { } ``` -# 55 二叉树的深度 +# 55.1 二叉树的深度 + +## 题目描述 + +从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。 + +

+ +## 解题思路 ```java public int TreeDepth(TreeNode root) { @@ -2189,6 +2160,34 @@ public int TreeDepth(TreeNode root) { } ``` +# 55.2 平衡二叉树 + +## 题目描述 + +平衡二叉树左右子树高度差不超过 1。 + +

+ +## 解题思路 + +```java +private boolean isBalanced = true; + +public boolean IsBalanced_Solution(TreeNode root) { + height(root); + return isBalanced; +} + +private int height(TreeNode root) { + if (root == null) return 0; + int left = height(root.left); + int right = height(root.right); + if (Math.abs(left - right) > 1) isBalanced = false; + return 1 + Math.max(left, right); +} +``` + + # 56. 数组中只出现一次的数字 ## 题目描述 @@ -2220,7 +2219,7 @@ public void FindNumsAppearOnce(int[] array, int num1[], int num2[]) { ## 题目描述 -输入一个递增排序的数组和一个数字 S,在数组中查找两个数,是的他们的和正好是 S,如果有多对数字的和等于 S,输出两个数的乘积最小的。 +输入一个递增排序的数组和一个数字 S,在数组中查找两个数,使得他们的和正好是 S,如果有多对数字的和等于 S,输出两个数的乘积最小的。 ## 解题思路 @@ -2252,26 +2251,25 @@ public ArrayList FindNumbersWithSum(int[] array, int sum) { ```java public ArrayList> FindContinuousSequence(int sum) { ArrayList> ret = new ArrayList<>(); - int start = 1, end = 2; - int mid = sum / 2; + int first = 1, last = 2; int curSum = 3; - while (start <= mid && end < sum) { + while (first <= sum / 2 && last < sum) { if (curSum > sum) { - curSum -= start; - start++; + curSum -= first; + first++; } else if (curSum < sum) { - end++; - curSum += end; + last++; + curSum += last; } else { ArrayList list = new ArrayList<>(); - for (int i = start; i <= end; i++) { + for (int i = first; i <= last; i++) { list.add(i); } ret.add(list); - curSum -= start; - start++; - end++; - curSum += end; + curSum -= first; + first++; + last++; + curSum += last; } } return ret; @@ -2295,25 +2293,22 @@ public String ReverseSentence(String str) { if (str.length() == 0) return str; int n = str.length(); char[] chars = str.toCharArray(); - int start = 0, end = 0; - while (end <= n) { - if (end == n || chars[end] == ' ') { - reverse(chars, start, end - 1); - start = end + 1; + int i = 0, j = 0; + while (j <= n) { + if (j == n || chars[j] == ' ') { + reverse(chars, i, j - 1); + i = j + 1; } - end++; + j++; } reverse(chars, 0, n - 1); return new String(chars); } -private void reverse(char[] c, int start, int end) { - while (start < end) { - char t = c[start]; - c[start] = c[end]; - c[end] = t; - start++; - end--; +private void reverse(char[] c, int i, int j) { + while(i < j) { + char t = c[i]; c[i] = c[j]; c[j] = t; + i++; j--; } } ``` @@ -2327,22 +2322,19 @@ private void reverse(char[] c, int start, int end) { ## 解题思路 ```java -public String LeftRotateString(String str, int k) { - if (str.length() == 0) return ""; +public String LeftRotateString(String str, int n) { + if(str.length() == 0) return ""; char[] c = str.toCharArray(); - reverse(c, 0, k - 1); - reverse(c, k, c.length - 1); + reverse(c, 0, n - 1); + reverse(c, n, c.length - 1); reverse(c, 0, c.length - 1); return new String(c); } private void reverse(char[] c, int i, int j) { - while (i < j) { - char t = c[i]; - c[i] = c[j]; - c[j] = t; - i++; - j--; + while(i < j) { + char t = c[i]; c[i] = c[j]; c[j] = t; + i++; j--; } } ``` @@ -2382,7 +2374,7 @@ public ArrayList maxInWindows(int[] num, int size) { ### 动态规划解法 -空间复杂度:O(n2) +空间复杂度:O(N2) ```java private static int face = 6; @@ -2410,7 +2402,7 @@ public double countProbability(int n, int s) { ### 动态规划解法 + 旋转数组 -空间复杂度:O(n) +空间复杂度:O(N) ```java private static int face = 6; @@ -2434,7 +2426,7 @@ public double countProbability(int n, int s) { flag = 1 - flag; } int totalNum = (int) Math.pow(6, n); - return (double) dp[n - 1][s - 1] / totalNum; + return (double) dp[flag][s - 1] / totalNum; } ``` @@ -2447,15 +2439,15 @@ public double countProbability(int n, int s) { ## 解题思路 ```java -public boolean isContinuous(int [] numbers) { - if(numbers.length < 5) return false; - Arrays.sort(numbers); +public boolean isContinuous(int[] nums) { + if (nums.length < 5) return false; + Arrays.sort(nums); int cnt = 0; - for(int num : numbers) if(num == 0) cnt++; - for(int i = cnt; i < numbers.length - 1; i++) { - if(numbers[i + 1] == numbers[i]) return false; - int interval = numbers[i + 1] - numbers[i] - 1; - if(interval > cnt) return false; + 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; + int interval = nums[i + 1] - nums[i] - 1; + if (interval > cnt) return false; cnt -= interval; } return true; @@ -2466,11 +2458,11 @@ public boolean isContinuous(int [] numbers) { ## 题目描述 -让小朋友们围成一个大圈。然后 , 他随机指定一个数 m, 让编号为 0 的小朋友开始报数。每次喊到 m-1 的那个小朋友要出列唱首歌 , 然后可以在礼品箱中任意的挑选礼物 , 并且不再回到圈中 , 从他的下一个小朋友开始 , 继续 0...m-1 报数 .... 这样下去 .... 直到剩下最后一个小朋友 , 可以不用表演。 +让小朋友们围成一个大圈。然后,他随机指定一个数 m,让编号为 0 的小朋友开始报数。每次喊到 m-1 的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续 0...m-1 报数 .... 这样下去 .... 直到剩下最后一个小朋友,可以不用表演。 ## 解题思路 -约瑟夫环 +约瑟夫环,圆圈长度为 n 的解可以看成长度为 n-1 的解再加上报数的长度 m。因为是圆圈,所以最后需要对 n 取余。 ```java public int LastRemaining_Solution(int n, int m) { @@ -2488,6 +2480,8 @@ public int LastRemaining_Solution(int n, int m) { ## 解题思路 +使用贪心策略,假设第 i 轮进行卖出操作,买入操作价格应该是 i 之前并且价格最低。 + ```java public int maxProfit(int[] prices) { int n = prices.length; @@ -2520,7 +2514,11 @@ public int Sum_Solution(int n) { # 65. 不用加减乘除做加法 -a ^ b 表示没有考虑进位的情况下两数的和,(a & b) << 1 就是进位。递归会终止的原因是 (a & b) << 1 最右边会多一个 0,那么继续递归,进位最右边的 0 会慢慢增多,最后进位会变为 0,递归终止。 +## 解题思路 + +a ^ b 表示没有考虑进位的情况下两数的和,(a & b) << 1 就是进位。 + +递归会终止的原因是 (a & b) << 1 最右边会多一个 0,那么继续递归,进位最右边的 0 会慢慢增多,最后进位会变为 0,递归终止。 ```java public int Add(int num1, int num2) { @@ -2553,6 +2551,8 @@ public int[] multiply(int[] A) { # 67. 把字符串转换成整数 +## 解题思路 + ```java public int StrToInt(String str) { if (str.length() == 0) return 0; @@ -2561,7 +2561,7 @@ public int StrToInt(String str) { int ret = 0; for (int i = 0; i < chars.length; i++) { if (i == 0 && (chars[i] == '+' || chars[i] == '-')) continue; - if (chars[i] < '0' || chars[i] > '9') return 0; + if (chars[i] < '0' || chars[i] > '9') return 0; // 非法输入 ret = ret * 10 + (chars[i] - '0'); } return isNegative ? -ret : ret; @@ -2570,7 +2570,13 @@ public int StrToInt(String str) { # 68. 树中两个节点的最低公共祖先 -树是二叉查找树的最低公共祖先问题: +## 解题思路 + +### 二叉查找树 + +

+ +二叉查找树中,两个节点 p, q 的公共祖先 root 满足 p.val <= root.val && root.val <= q.val,只要找到满足这个条件的最低层节点即可。换句话说,应该先考虑子树的解而不是根节点的解,二叉树的后序遍历操作满足这个特性。在本题中我们可以利用后序遍历的特性,先在左右子树中查找解,最后再考虑根节点的解。 ```java public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { @@ -2579,3 +2585,22 @@ public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { return root; } ``` + +### 普通二叉树 + +

+ +在左右子树中查找两个节点的最低公共祖先,如果在其中一颗子树中查找到,那么就返回这个解,否则可以认为根节点就是最低公共祖先。 + +```java +public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { + if (root == null || root == p || root == q) return root; + TreeNode left = lowestCommonAncestor(root.left, p, q); + TreeNode right = lowestCommonAncestor(root.right, p, q); + return left == null ? right : right == null ? left : root; +} +``` + +# 参考文献 + +- 何海涛. 剑指 Offer[M]. 电子工业出版社, 2012. diff --git a/notes/数据库系统原理.md b/notes/数据库系统原理.md index cd13daa5..081ad4b4 100644 --- a/notes/数据库系统原理.md +++ b/notes/数据库系统原理.md @@ -28,7 +28,12 @@ * [数据库的三层模式和两层映像](#数据库的三层模式和两层映像) * [九、关系数据库建模](#九关系数据库建模) * [ER 图](#er-图) - * [十、约束](#十约束) +* [十、约束](#十约束) + * [1. 键码](#1-键码) + * [2. 单值约束](#2-单值约束) + * [3. 引用完整性约束](#3-引用完整性约束) + * [4. 域约束](#4-域约束) + * [5. 一般约束](#5-一般约束) * [参考资料](#参考资料) @@ -37,13 +42,13 @@ ## 概念 -

+

事务指的是满足 ACID 特性的一系列操作。在数据库中,可以通过 Commit 提交一个事务,也可以使用 Rollback 进行回滚。 ## 四大特性 -

+

### 1. 原子性(Atomicity) @@ -71,23 +76,25 @@ T1 和 T2 两个事务都对一个数据进行修改,T1 先修改,T2 随后修改,T2 的修改覆盖了 T1 的修改。 +

+ ### 2. 读脏数据 T1 修改一个数据,T2 随后读取这个数据。如果 T1 撤销了这次修改,那么 T2 读取的数据是脏数据。 -

+

### 3. 不可重复读 T2 读取一个数据,T1 对该数据做了修改。如果 T2 再次读取这个数据,此时读取的结果和和第一次读取的结果不同。 -

+

### 4. 幻影读 T1 读取某个范围的数据,T2 在这个范围内插入新的数据,T1 再次读取这个范围的数据,此时读取的结果和和第一次读取的结果不同。 -

+

## 解决方法 @@ -125,8 +132,8 @@ MySQL 中提供了两种封锁粒度:行级锁以及表级锁。 | - | X | S | | :--: | :--: | :--: | -|X|Yes|No| -|S|No|No| +|X|No|No| +|S|No|Yes| ### 2. 意向锁 @@ -152,7 +159,7 @@ MySQL 中提供了两种封锁粒度:行级锁以及表级锁。 **一级封锁协议** -事务 T 要修改数据 A 时必须加 X 锁,直到事务结束才释放锁。 +事务 T 要修改数据 A 时必须加 X 锁,直到 T 结束才释放锁。 可以解决丢失修改问题,因为不能同时有两个事务对同一个数据进行修改,那么一个事务的修改就不会被覆盖。 @@ -215,7 +222,7 @@ MySQL 中提供了两种封锁粒度:行级锁以及表级锁。 ### 2. 两段锁协议 -加锁和解锁分为两个阶段进行,事务 T 对数据 A 进行读或者写操作之前,必须先获得对 A 的封锁,并且在释放一个封锁之前,T 不能再获得任何的其它锁。 +加锁和解锁分为两个阶段进行,事务 T 对数据 A 进行读或者写操作之前,必须先获得对 A 的封锁,并且在释放一个封锁之后,T 不能再获得任何的其它锁。 事务遵循两段锁协议是保证并发操作可串行化调度的充分条件。例如以下操作满足两段锁协议,它是可串行化调度。 @@ -258,9 +265,7 @@ lock-x(A)...unlock(A)...lock-s(B)...unlock(B)...lock-s(c)...unlock(C)... # 五、多版本并发控制 -(Multi-Version Concurrency Control, MVCC)是 MySQL 的 InnoDB 存储引擎实现隔离级别的一种具体方式,它的基本思想是通过保存每个数据行的多个版本,一个事务对数据行做修改时,其它事务可以读取之前的一个版本,并且都是读取相同的版本,从而保证多个事务对同一个数据行读取的结果是一致的。 - -用于实现提交读和可重复读这两种隔离级别。而对于未提交读隔离级别,它总是读取最新的数据行,无需使用 MVCC;可串行化隔离级别需要对所有读取的行都加锁,单纯使用 MVCC 无法实现。 +(Multi-Version Concurrency Control, MVCC)是 MySQL 的 InnoDB 存储引擎实现隔离级别的一种具体方式,用于实现提交读和可重复读这两种隔离级别。而未提交读隔离级别总是读取最新的数据行,无需使用 MVCC;可串行化隔离级别需要对所有读取的行都加锁,单纯使用 MVCC 无法实现。 ## 版本号 @@ -280,15 +285,17 @@ InnoDB 的 MVCC 使用到的快照存储在 Undo 日志中,该日志通过回 ## 实现过程 +以下过程针对可重复读隔离级别。 + ### 1. SELECT -该操作必须保证多个事务读取到同一个数据行的快照。但是也有例外,如果有一个事务正在修改该数据行,那么它可以读取事务本身所做的修改,而不用和其它事务的读取结果一致。 +该操作必须保证多个事务读取到同一个数据行的快照,这个快照是最近的一个有效快照。但是也有例外,如果有一个事务正在修改该数据行,那么它可以读取事务本身所做的修改,而不用和其它事务的读取结果一致。 -当开始新一个事务时,该事务的版本号肯定会大于所有数据行快照的创建版本号,理解这一点很关键。 +当开始新一个事务时,该事务的版本号肯定会大于当前所有数据行快照的创建版本号,理解这一点很关键。 -把没对一个数据行做修改的事务称为 T1,T1 所要读取的数据行快照的创建版本号必须小于当前事务的版本号,因为如果大于或者等于当前事务的版本号,那么表示该数据行快照是其它事务的最新修改,因此不能去读取它。 +把没对一个数据行做修改的事务称为 T,T 所要读取的数据行快照的创建版本号必须小于 T 的版本号,因为如果大于或者等于 T 的版本号,那么表示该数据行快照是其它事务的最新修改,因此不能去读取它。 -除了上面的要求,T1 所要读取的数据行快照的删除版本号必须小于当前事务版本号,因为如果大于或者等于当前事务版本号,那么表示该数据行快照是已经被删除的,不应该去读取它。 +除了上面的要求,T 所要读取的数据行快照的删除版本号必须大于 T 的版本号,因为如果小于等于 T 的版本号,那么表示该数据行快照是已经被删除的,不应该去读取它。 ### 2. INSERT @@ -300,7 +307,7 @@ InnoDB 的 MVCC 使用到的快照存储在 Undo 日志中,该日志通过回 ### 4. UPDATE -将系统版本号作为更新后的数据行快照的创建版本号,同时将系统版本号作为作为更新前的数据行快照的删除版本号。可以理解为新执行 DELETE 后执行 INSERT。 +将系统版本号作为更新后的数据行快照的创建版本号,同时将系统版本号作为作为更新前的数据行快照的删除版本号。可以理解为先执行 DELETE 后执行 INSERT。 ## 快照读与当前读 @@ -322,22 +329,22 @@ update ; delete; ``` -引入当前读的目的主要是为了免去加锁操作带来的性能开销,但是快照读需要加锁。 +引入快照读的目的主要是为了免去加锁操作带来的性能开销,但是当前读需要加锁。 # 六、Next-Key Locks -Next-Key Locks 也是 MySQL 的 InnoDB 存储引擎的一种实现。MVCC 不能解决幻读的问题,Next-Key Locks 就是为了解决这个问题而存在的。在可重复读隔离级别下,MVCC + Next-Key Locks,就可以防止幻读的出现。 +Next-Key Locks 也是 MySQL 的 InnoDB 存储引擎的一种锁实现。MVCC 不能解决幻读的问题,Next-Key Locks 就是为了解决这个问题而存在的。在可重复读隔离级别下,MVCC + Next-Key Locks,就可以防止幻读的出现。 ## Record Locks -锁定的对象时索引,而不是数据。如果表没有设置索引,InnoDB 会自动在主键上创建隐藏的聚集索引,因此 Record Lock 依然可以使用。 +锁定的对象是索引,而不是数据。如果表没有设置索引,InnoDB 会自动在主键上创建隐藏的聚集索引,因此 Record Locks 依然可以使用。 ## Grap Locks -锁定一个范围内的索引,例如当一个事务执行以下语句,其它事务就不能在 t.c1 中插入 15。 +锁定一个范围内的索引,例如当一个事务执行以下语句,其它事务就不能在 t.c 中插入 15。 ```sql -SELECT c1 FROM t WHERE c1 BETWEEN 10 and 20 FOR UPDATE; +SELECT c FROM t WHERE c BETWEEN 10 and 20 FOR UPDATE; ``` ## Next-Key Locks @@ -571,34 +578,34 @@ Entity-Relationship,有三个组成部分:实体、属性、联系。

-## 十、约束 +# 十、约束 -### 1. 键码 +## 1. 键码 用于唯一表示一个实体。 键码可以由多个属性构成,每个构成键码的属性称为码。 -### 2. 单值约束 +## 2. 单值约束 某个属性的值是唯一的。 -### 3. 引用完整性约束 +## 3. 引用完整性约束 一个实体的属性引用的值在另一个实体的某个属性中存在。 -### 4. 域约束 +## 4. 域约束 某个属性的值在特定范围之内。 -### 5. 一般约束 +## 5. 一般约束 比如大小约束,数量约束。 # 参考资料 - 史嘉权. 数据库系统概论[M]. 清华大学出版社有限公司, 2006. -- 施瓦茨. 高性能MYSQL(第3版)[M]. 电子工业出版社, 2013. +- 施瓦茨. 高性能 MYSQL(第3版)[M]. 电子工业出版社, 2013. - [The InnoDB Storage Engine](https://dev.mysql.com/doc/refman/5.7/en/innodb-storage-engine.html) - [Transaction isolation levels](https://www.slideshare.net/ErnestoHernandezRodriguez/transaction-isolation-levels) - [Concurrency Control](http://scanftree.com/dbms/2-phase-locking-protocol) diff --git a/notes/正则表达式.md b/notes/正则表达式.md index 74c20087..d890273b 100644 --- a/notes/正则表达式.md +++ b/notes/正则表达式.md @@ -9,6 +9,7 @@ * [八、回溯引用](#八回溯引用) * [九、前后查找](#九前后查找) * [十、嵌入条件](#十嵌入条件) +* [参考资料](#参考资料) @@ -18,8 +19,6 @@ 正则表达式内置于其它语言或者软件产品中,它本身不是一种语言或者软件。 -一个问题往往可以用多种正则表达式方案来解决。 - [正则表达式在线工具](http://tool.chinaz.com/regex) # 二、匹配单个字符 @@ -44,7 +43,9 @@ My **name** is Zheng. **[ ]** 定义一个字符集合; -0-9、a-z 定义了一个字符区间,区间使用 ASCII 码来确定。字符区间只能用在 [ ] 之间,因此 **-** 元字符只有在 [ ] 之间才是元字符,在 [ ] 之外就是一个普通字符; +0-9、a-z 定义了一个字符区间,区间使用 ASCII 码来确定,字符区间只能用在 [ ] 之间。 + +**-** 元字符只有在 [ ] 之间才是元字符,在 [ ] 之外就是一个普通字符; **^** 是取非操作,必须在 [ ] 字符集合中使用; @@ -69,7 +70,7 @@ abc[^0-9] ## 匹配空白字符 | 元字符 | 说明 | -| ------------ | ------------ | +| :---: | :---: | | [\b] | 回退(删除)一个字符 | | \f | 换页符 | | \n | 换行符 | @@ -79,53 +80,33 @@ abc[^0-9] \r\n 是 Windows 中的文本行结束标签,在 Unix/Linux 则是 \n ;\r\n\r\n 可以匹配 Windows 下的空白行,因为它将匹配两个连续的行尾标签,而这正是两条记录之间的空白行; -. 是元字符,前提是没有对它们进行转义; f 和 n 也是元字符,但是前提是对他们进行了转义。 +. 是元字符,前提是没有对它们进行转义;f 和 n 也是元字符,但是前提是对它们进行了转义。 ## 匹配特定的字符类别 ### 1. 数字元字符 | 元字符 | 说明 | -| ------------ | ------------ | +| :---: | :---: | | \d | 数字字符,等价于 [0-9] | | \D | 非数字字符,等价于 [^0-9] | ### 2. 字母数字元字符 | 元字符 | 说明 | -| ------------ | ------------ | +| :---: | :---: | | \w | 大小写字母,下划线和数字,等价于 [a-zA-Z0-9\_] | | \W | 对 \w 取非 | ### 3. 空白字符元字符 | 元字符 | 说明 | -| ------------ | ------------ | +| :---: | :---: | | \s | 任何一个空白字符,等价于 [\f\n\r\t\v] | | \S | 对 \s 取非 | \x 匹配十六进制字符,\0 匹配八进制,例如 \x0A 对应 ASCII 字符 10 ,等价于 \n,也就是它会匹配 \n 。 -## 使用 POSIX 字符类 - -| 字符类 | 说明 | -| --- | --- | -| [:alnum:] | 字母数字字符 | -| [:alpha:] | 字母字符 | -| [:cntrl:] | 控制字符 | -| [:digit:] | 数字字符 | -| [:graph:] | 非空白字符 ( 非空格、控制字符等 ) | -| [:lower:] | 小写字母 | -| [:print:] | 与 [:graph:] 相似,但是包含空格字符 | -| [:punct:] | 标点字符 | -| [:space:] | 所有的空白字符 ( 换行符、空格、制表符 ) | -| [:upper:] | 大写字母 | -| [:xdigit:] | 允许十六进制的数字 (0-9a-fA-F) | - -并不是所有正则表达式实现都支持 POSIX 字符类,也不一定使用它。 - -使用时需要用两对方括号,例如 [[:alpha:]]。 - # 五、重复匹配 **\+** 匹配 1 个或者多个字符, **\*** 匹配 0 个或者多个,**?** 匹配 0 个或者 1 个。 @@ -149,8 +130,8 @@ abc[^0-9] 为了可读性,常常把转义的字符放到字符集合 [ ] 中,但是含义是相同的。 ``` -\w+@\w+.\w+ -[\w]+@[\w]+.[\w]+ +[\w.]+@\w+.\w+ +[\w.]+@[\w]+.[\w]+ ``` **{n}** 匹配 n 个字符,**{m, n}** 匹配 m\~n 个字符,**{m,}** 至少匹配 m 个字符; @@ -195,7 +176,7 @@ a.+c (?m)^\s*//.*$ ``` -如果没用 (?m),则只会匹配 // 注释 1 以及之后的所有内容,因为 * 是贪婪型的。用了分行匹配模式之后,换行符被当成是字符串分隔符,因此能正确匹配出两个注释内容。 +如果没用 (?m),则只会匹配 // 注释 1 以及之后的所有内容。用了分行匹配模式之后,换行符被当成是字符串分隔符,因此能正确匹配出两个注释内容。 **匹配结果** @@ -257,7 +238,7 @@ a.+c # 八、回溯引用 -回溯引用使用 **\n** 来引用某个子表达式,其中 n 代表的是子表达式的序号,从 1 开始。它和子表达式匹配的内容一致,比如子表达式匹配到 abc ,那么回溯引用部分也需要匹配 abc 。 +回溯引用使用 **\n** 来引用某个子表达式,其中 n 代表的是子表达式的序号,从 1 开始。它和子表达式匹配的内容一致,比如子表达式匹配到 abc,那么回溯引用部分也需要匹配 abc 。 **应用** @@ -310,7 +291,7 @@ a.+c ## 大小写转换 | 元字符 | 说明 | -| ---| ---| +| :---: | :---: | | \l | 把下个字符转换为小写 | | \u| 把下个字符转换为大写 | | \L | 把\L 和\E 之间的字符全部转换为小写 | @@ -398,3 +379,7 @@ aBCd 1. **11111** 2. 22222- 3. **33333-4444** + +# 参考资料 + +- BenForta. 正则表达式必知必会 [M]. 人民邮电出版社, 2007. diff --git a/notes/算法.md b/notes/算法.md index 9377a9ab..ba6996e8 100644 --- a/notes/算法.md +++ b/notes/算法.md @@ -1,6 +1,5 @@ * [一、算法分析](#一算法分析) - * [函数转换](#函数转换) * [数学模型](#数学模型) * [ThreeSum](#threesum) * [倍率实验](#倍率实验) @@ -23,42 +22,27 @@ * [优先队列](#优先队列) * [应用](#应用) * [五、查找](#五查找) - * [符号表](#符号表) + * [二分查找实现有序符号表](#二分查找实现有序符号表) * [二叉查找树](#二叉查找树) * [2-3 查找树](#2-3-查找树) * [红黑二叉查找树](#红黑二叉查找树) * [散列表](#散列表) * [应用](#应用) +* [参考资料](#参考资料) # 一、算法分析 -## 函数转换 - -指数函数可以转换为线性函数,从而在函数图像上显示的更直观。例如 - -

- -可以在其两端同时取对数,得到: - -

- -

- ## 数学模型 ### 1. 近似 -使用 \~f(N) 来表示所有随着 N 的增大除以 f(N) 的结果趋近于 1 的函数,例如 N3/6-N2/2+N/3 \~ N3/6。 - -

+N3/6-N2/2+N/3 \~ N3/6。使用 \~f(N) 来表示所有随着 N 的增大除以 f(N) 的结果趋近于 1 的函数。 ### 2. 增长数量级 -增长数量级将算法与它的实现隔离开来,一个算法的增长数量级为 N3 与它是否用 Java 实现,是否运行于特定计算机上无关。 - -

+N3/6-N2/2+N/3 的增长数量级为 O(N3)。增长数量级将算法与它的实现隔离开来,一个算法的增长数量级为 O(N3) 与它是否用 Java 实现,是否运行于特定计算机上无关。 ### 3. 内循环 @@ -74,13 +58,13 @@ ThreeSum 用于统计一个数组中三元组的和为 0 的数量。 ```java public class ThreeSum { - public static int count(int[] a) { - int N = a.length; + public int count(int[] nums) { + int N = nums.length; int cnt = 0; for (int i = 0; i < N; i++) { for (int j = i + 1; j < N; j++) { for (int k = j + 1; k < N; k++) { - if (a[i] + a[j] + a[k] == 0) { + if (nums[i] + nums[j] + nums[k] == 0) { cnt++; } } @@ -91,7 +75,7 @@ public class ThreeSum { } ``` -该算法的内循环为`if (a[i] + a[j] + a[k] == 0)`语句,总共执行的次数为 N(N-1)(N-2) = N3/6 - N2/2 + N/3,因此它的近似执行次数为 \~N3/6,增长数量级为 N3。 +该算法的内循环为 if(a[i]+a[j]+a[k]==0) 语句,总共执行的次数为 N(N-1)(N-2) = N3/6-N2/2+N/3,因此它的近似执行次数为 \~N3/6,增长数量级为 N3 **改进**
@@ -101,21 +85,31 @@ public class ThreeSum { ```java public class ThreeSumFast { - public static int count(int[] a) { - Arrays.sort(a); - int N = a.length; + public int count(int[] nums) { + Arrays.sort(nums); + int N = nums.length; int cnt = 0; for (int i = 0; i < N; i++) { for (int j = i + 1; j < N; j++) { - // rank() 方法返回元素在数组中的下标,如果元素不存在,这里会返回 -1。 // 应该注意这里的下标必须大于 j,否则会重复统计。 - if (BinarySearch.rank(-a[i] - a[j], a) > j) { + if (binarySearch(nums, -nums[i] - nums[j]) > j) { cnt++; - } + } } } return cnt; } + + private int binarySearch(int[] nums, int target) { + int l = 0, h = nums.length - 1; + while (l <= h) { + int m = l + (h - l) / 2; + if (target == nums[m]) return m; + else if (target > nums[m]) h = m - 1; + else l = m + 1; + } + return -1; + } } ``` @@ -125,9 +119,16 @@ public class ThreeSumFast { 例如对于暴力方法的 ThreeSum 算法,近似时间为 \~N3/6。进行如下实验:多次运行该算法,每次取的 N 值为前一次的两倍,统计每次执行的时间,并统计本次运行时间与前一次运行时间的比值,得到如下结果: -

+| N | Time | Ratio | +| :---: | :---: | :---: | +| 250 | 0.0 | 2.7 | +| 500 | 0.0 | 4.8 | +| 1000 | 0.1 | 6.9 | +| 2000 | 0.8 | 7.7 | +| 4000 | 6.4 | 8.0 | +| 8000 | 51.1 | 8.0 | -可以看到,T(2N)/T(N) \~ 23,因此可以确定 T(N) \~ aN2logN。 +可以看到,T(2N)/T(N) \~ 23,因此可以确定 T(N) \~ aN3logN。 ## 注意事项 @@ -155,9 +156,7 @@ public class ThreeSumFast { ## 栈 -first-in-last-out(FILO) - -

+> First-In-Last-Out **1. 数组实现**
@@ -220,12 +219,6 @@ public class ResizeArrayStack implements Iterable { } ``` -上面实现使用了泛型,Java 不能直接创建泛型数组,只能使用转型来创建。 - -```java -Item[] arr = (Item[]) new Object[N]; -``` - **2. 链表实现**
需要使用链表的头插法来实现,因为头插法中最后压入栈的元素在链表的开头,它的 next 指针指向前一个压入栈的元素,在弹出元素使就可以通过 next 指针遍历到前一个压入栈的元素从而让这个元素称为新的栈顶元素。 @@ -265,11 +258,10 @@ public class Stack { } } ``` + ## 队列 -first-in-first-out(FIFO) - -

+> First-In-First-Out 下面是队列的链表实现,需要维护 first 和 last 节点指针,分别指向队首和队尾。 @@ -320,21 +312,21 @@ public class Queue { # 三、union-find - **概览**
用于解决动态连通性问题,能动态连接两个点,并且判断两个点是否连通。 -

+

- **API**
-

- - **基本数据结构**
+| 方法 | 描述 | +| :---: | :---: | +| UF(int N) | 构造一个大小为 N 的并查集 | +| void union(int p, int q) | 连接 p 和 q 节点 | +| int find(int p) | 查找 p 所在的连通分量 | +| boolean connected(int p, int q) | 判断 p 和 q 节点是否连通 | ```java public class UF { - // 使用 id 数组来保存点的连通信息 private int[] id; public UF(int N) { @@ -352,30 +344,36 @@ public class UF { ## quick-find -保证在同一连通分量的所有节点的 id 值相等。 +可以快速进行 find 操作,即可以快速判断两个节点是否连通。 -这种方法可以快速取得一个节点的 id 值,并且判断两个节点是否连通。但是 union 的操作代价却很高,需要将其中一个连通分量中的所有节点 id 值都修改为另一个节点的 id 值。 +同一连通分量的所有节点的 id 值相等。 + +但是 union 操作代价却很高,需要将其中一个连通分量中的所有节点 id 值都修改为另一个节点的 id 值。 + +

```java - public int find(int p) { - return id[p]; - } - public void union(int p, int q) { - int pID = find(p); - int qID = find(q); +public int find(int p) { + return id[p]; +} +public void union(int p, int q) { + int pID = find(p); + int qID = find(q); - if (pID == qID) return; - for (int i = 0; i < id.length; i++) { - if (id[i] == pID) id[i] = qID; - } + if (pID == qID) return; + for (int i = 0; i < id.length; i++) { + if (id[i] == pID) id[i] = qID; } +} ``` ## quick-union -在 union 时只将节点的 id 值指向另一个节点 id 值,不直接用 id 来存储所属的连通分量。这样就构成一个倒置的树形结构,应该注意的是根节点需要指向自己。查找一个节点所属的连通分量时,要一直向上查找直到根节点,并使用根节点的 id 值作为本连通分量的 id 值。 +可以快速进行 union 操作,只需要修改一个节点的 id 值即可。 -

+但是 find 操作开销很大,因为同一个连通分量的节点 id 值不同,id 值只是用来指向另一个节点。因此需要一直向上查找操作,直到找到最上层的节点。 + +

```java public int find(int p) { @@ -393,7 +391,7 @@ public class UF { 这种方法可以快速进行 union 操作,但是 find 操作和树高成正比,最坏的情况下树的高度为触点的数目。 -

+

## 加权 quick-union @@ -401,7 +399,7 @@ public class UF { 理论研究证明,加权 quick-union 算法构造的树深度最多不超过 logN。 -

+

```java public class WeightedQuickUnionUF { @@ -448,7 +446,12 @@ public class WeightedQuickUnionUF { ## 各种 union-find 算法的比较 -

+| 算法 | union | find | +| :---: | :---: | :---: | +| quick-find | N | 1 | +| quick-union | 树高 | 树高 | +| 加权 quick-union | lgN | lgN | +| 路径压缩的加权 quick-union | 非常接近 1 | 非常接近 1 | # 四、排序 @@ -476,7 +479,7 @@ private void exch(Comparable[] a, int i, int j){ 找到数组中的最小元素,将它与数组的第一个元素交换位置。再从剩下的元素中找到最小的元素,将它与数组的第二个元素交换位置。不断进行这样的操作,直到将整个数组排序。 -

+

```java public class Selection { @@ -499,7 +502,7 @@ public class Selection { 插入排序从左到右进行,每次都将当前元素插入到左部已经排序的数组中,使得插入之后左部数组依然有序。 -

+

```java public class Insertion { @@ -518,10 +521,6 @@ public class Insertion { 插入排序对于部分有序数组和小规模数组特别高效。 - **选择排序和插入排序的比较**
- -对于随机排列的无重复主键的数组,插入排序和选择排序的运行时间是平方级别的,两者之比是一个较小的常数。 - ## 希尔排序 对于大规模的数组,插入排序很慢,因为它只能交换相邻的元素,如果要把元素从一端移到另一端,就需要很多次操作。 @@ -530,9 +529,7 @@ public class Insertion { 希尔排序使用插入排序对间隔 h 的序列进行排序,如果 h 很大,那么元素就能很快的移到很远的地方。通过不断减小 h,最后令 h=1,就可以使得整个数组是有序的。 -

- -

+

```java public class Shell { @@ -560,9 +557,7 @@ public class Shell { 归并排序的思想是将数组分成两部分,分别进行排序,然后归并起来。 -

- -

+

### 1. 归并方法 @@ -591,7 +586,8 @@ public class MergeSort { ### 2. 自顶向下归并排序 -

+

+ ```java public static void sort(Comparable[] a) { @@ -610,14 +606,12 @@ private static void sort(Comparable[] a, int lo, int hi) { 因为每次都将问题对半分成两个子问题,而这种对半分的算法复杂度一般为 O(NlogN),因此该归并排序方法的时间复杂度也为 O(NlogN)。 -因为小数组的递归操作会过于频繁,因此可以在数组过小时切换到插入排序来提高性能。 +小数组的递归操作会过于频繁,可以在数组过小时切换到插入排序来提高性能。 ### 3. 自底向上归并排序 先归并那些微型数组,然后成对归并得到的子数组。 -

- ```java public static void busort(Comparable[] a) { int N = a.length; @@ -636,7 +630,7 @@ public static void busort(Comparable[] a) { 归并排序将数组分为两个子数组分别排序,并将有序的子数组归并使得整个数组排序;快速排序通过一个切分元素将数组分为两个子数组,左子数组小于等于切分元素,右子数组大于等于切分元素,将这两个子数组排序也就将整个数组排序了。 -

+

```java public class QuickSort { @@ -656,9 +650,9 @@ public class QuickSort { ### 2. 切分 -取 a[lo] 作为切分元素,然后从数组的左端向右扫描直到找到第一个大于等于它的元素,再从数组的右端向左扫描找到第一个小于等于它的元素,交换这两个元素,并不断进行这个过程,就可以保证左指针 i 的左侧元素都不大于切分元素,右指针 j 的右侧元素都不小于切分元素。当两个指针相遇时,将切分元素 a[lo] 和左子数组最右侧的元素 a[j] 交换然后返回 j 即可。 +取 a[lo] 作为切分元素,然后从数组的左端向右扫描直到找到第一个大于等于它的元素,再从数组的右端向左扫描找到第一个小于等于它的元素,交换这两个元素,并不断进行这个过程,就可以保证左指针 i 的左侧元素都不大于切分元素,右指针 j 的右侧元素都不小于切分元素。当两个指针相遇时,将切分元素 a[lo] 和 a[j] 交换位置。 -

+

```java private static int partition(Comparable[] a, int lo, int hi) { @@ -699,8 +693,6 @@ private static int partition(Comparable[] a, int lo, int hi) { 三向切分快速排序对于只有若干不同主键的随机数组可以在线性时间内完成排序。 -

- ```java public class Quick3Way { public static void sort(Comparable[] a, int lo, int hi) { @@ -725,11 +717,12 @@ public class Quick3Way { ### 1. 堆 -定义:一颗二叉树的每个节点都大于等于它的两个子节点。 +堆的某个节点的值总是大于等于子节点的值,并且堆是一颗完全二叉树。 + 堆可以用数组来表示,因为堆是一种完全二叉树,而完全二叉树很容易就存储在数组中。位置 k 的节点的父节点位置为 k/2,而它的两个子节点的位置分别为 2k 和 2k+1。这里我们不使用数组索引为 0 的位置,是为了更清晰地理解节点的关系。 -

+

```java public class MaxPQ { @@ -764,7 +757,7 @@ public class MaxPQ { 在堆中,当一个节点比父节点大,那么需要交换这个两个节点。交换后还可能比它新的父节点大,因此需要不断地进行比较和交换操作。把这种操作称为上浮。 -

+

```java private void swim(int k) { @@ -777,7 +770,7 @@ private void swim(int k) { 类似地,当一个节点比子节点来得小,也需要不断的向下比较和交换操作,把这种操作称为下沉。一个节点有两个子节点,应当与两个子节点中最大那么节点进行交换。 -

+

```java private void sink(int k) { @@ -820,11 +813,19 @@ public Key delMax() { 由于堆可以很容易得到最大的元素并删除它,不断地进行这种操作可以得到一个递减序列。如果把最大元素和当前堆中数组的最后一个元素交换位置,并且不删除它,那么就可以得到一个从尾到头的递减序列,从正向来看就是一个递增序列。因此很容易使用堆来进行排序,并且堆排序是原地排序,不占用额外空间。 -堆排序要分两个阶段,第一个阶段是把无序数组建立一个堆;第二个阶段是交换最大元素和当前堆的数组最后一个元素,并且进行下沉操作维持堆的有序状态。 +**构建堆** 无序数组建立堆最直接的方法是从左到右遍历数组,然后进行上浮操作。一个更高效的方法是从右至左进行下沉操作,如果一个节点的两个节点都已经是堆有序,那么进行下沉操作可以使得这个节点为根节点的堆有序。叶子节点不需要进行下沉操作,因此可以忽略叶子节点的元素,因此只需要遍历一半的元素即可。 -

+

+ +**交换堆顶元素与最后一个元素** + +交换之后需要进行下沉操作维持堆的有序状态。 + +

+ +

```java public static void sort(Comparable[] a){ @@ -853,9 +854,17 @@ public static void sort(Comparable[] a){ ### 1. 排序算法的比较 -

+| 算法 | 稳定 | 原地排序 | 时间复杂度 | 空间复杂度 | 备注 | +| :---: | :---: | :---: | :---: | :---: | :---: | +| 选择排序 | no | yes | N2 | 1 | | +| 插入排序 | yes | yes | N \~ N2 | 1 | 时间复杂度和初始顺序有关 | +| 希尔排序 | no | yes | N 的若干倍乘于递增序列的长度 | 1 | | +| 快速排序 | no | yes | NlogN | logN | | +| 三向切分快速排序 | no | yes | N \~ NlogN | logN | 适用于有大量重复主键| +| 归并排序 | yes | no | NlogN | N | | +| 堆排序 | no | yes | NlogN | 1 | | | -快速排序时最快的通用排序算法,它的内循环的指令很少,而且它还能利用缓存,因为它总是顺序地访问数据。它的运行时间近似为 \~cNlogN,这里的 c 比其他线性对数级别的排序算法都要小。使用三向切分之后,实际应用中可能出现的某些分布的输入能够达到线性级别,而其它排序算法仍然需要线性对数时间。 +快速排序是最快的通用排序算法,它的内循环的指令很少,而且它还能利用缓存,因为它总是顺序地访问数据。它的运行时间近似为 \~cNlogN,这里的 c 比其他线性对数级别的排序算法都要小。使用三向切分之后,实际应用中可能出现的某些分布的输入能够达到线性级别,而其它排序算法仍然需要线性对数时间。 ### 2. Java 的排序算法实现 @@ -884,25 +893,15 @@ public static Comparable select(Comparable[] a, int k) { # 五、查找 -符号表是一种存储键值对的数据结构,支持两种操作:插入一个新的键值对;根据给定键得到值。 +符号表是一种存储键值对的数据结构,主要支持两种操作:插入一个新的键值对、根据给定键得到值。 -## 符号表 - -### 1. 无序符号表 - -

- -### 2. 有序符号表 - -

- -有序指的是支持 min() max() 等根据键的大小关系来实现的操作。 +符号表分为有序和无序两种,有序符号表主要指支持 min()、max() 等根据键的大小关系来实现的操作。 有序符号表的键需要实现 Comparable 接口。 -### 3. 二分查找实现有序符号表 +## 二分查找实现有序符号表 -使用一对平行数组,一个存储键一个存储值。其中键的数组为 Comparable 数组,值的数字为 Object 数组。 +使用一对平行数组,一个存储键一个存储值。其中键的数组为 Comparable 数组,值的数组为 Object 数组。 rank() 方法至关重要,当键在表中时,它能够知道该键的位置;当键不在表中时,它也能知道在何处插入新键。 @@ -967,17 +966,17 @@ public class BinarySearchST, Value> { ## 二叉查找树 -**二叉树** 定义为一个空链接,或者是一个有左右两个链接的节点,每个链接都指向一颗子二叉树。 +**二叉树** 是一个空链接,或者是一个有左右两个链接的节点,每个链接都指向一颗子二叉树。 -

+

**二叉查找树** (BST)是一颗二叉树,并且每个节点的值都大于其左子树中的所有节点的值而小于右子树的所有节点的值。 -

+

-BST 有一个重要性质,就是它的前序遍历结果递增排序。 +BST 有一个重要性质,就是它的中序遍历结果递增排序。 -

+

基本数据结构: @@ -1010,13 +1009,15 @@ public class BST, Value> { } ``` +(为了方便绘图,二叉树的空链接不画出来。) + ### 1. get() - 如果树是空的,则查找未命中; - 如果被查找的键和根节点的键相等,查找命中; - 否则递归地在子树中查找:如果被查找的键较小就在左子树中查找,较大就在右子树中查找。 -BST 的查找操作每次递归都会让区间减少一半,和二分查找类似,因此查找的复杂度为 O(logn)。 +BST 的查找操作每次递归都会让区间减少一半,和二分查找类似,因此查找的复杂度为 O(logN)。 ```java public Value get(Key key) { @@ -1035,7 +1036,7 @@ private Value get(Node x, Key key) { 当插入的键不存在于树中,需要创建一个新节点,并且更新上层节点的链接使得该节点正确链接到树中。 -

+

```java public void put(Key key, Value val) { @@ -1054,9 +1055,13 @@ private Node put(Node x, Key key, Value val) { ### 3. 分析 -二叉查找树的算法运行时间取决于树的形状,而树的形状又取决于键被插入的先后顺序。最好的情况下树是完全平衡的,每条空链接和根节点的距离都为 logN。在最坏的情况下,树的高度为 N。 +二叉查找树的算法运行时间取决于树的形状,而树的形状又取决于键被插入的先后顺序。最好的情况下树是完全平衡的,每条空链接和根节点的距离都为 logN。 -

+

+ +在最坏的情况下,树的高度为 N。 + +

### 4. floor() @@ -1065,7 +1070,6 @@ floor(key):小于等于键的最大键 - 如果键小于根节点的键,那么 floor(key) 一定在左子树中; - 如果键大于根节点的键,需要先判断右子树中是否存在 floor(key),如果存在就找到,否则根节点就是 floor(key)。 -

```java public Key floor(Key key) { @@ -1121,7 +1125,7 @@ private Node min(Node x) { 令指向最小节点的链接指向最小节点的右子树。 -

+

```java public void deleteMin() { @@ -1140,7 +1144,7 @@ public Node deleteMin(Node x) { - 如果待删除的节点只有一个子树,那么只需要让指向待删除节点的链接指向唯一的子树即可; - 否则,让右子树的最小节点替换该节点。 -

+

```java public void delete(Key key) { @@ -1190,7 +1194,7 @@ private void keys(Node x, Queue queue, Key lo, Key hi) { ## 2-3 查找树 -

+

2-3 查找树引入了 2- 节点和 3- 节点,目的是为了让树更平衡。一颗完美平衡的 2-3 查找树的所有空链接到根节点的距离应该是相同的。 @@ -1202,11 +1206,11 @@ private void keys(Node x, Queue queue, Key lo, Key hi) { 插入到 2- 节点上,那么直接将新节点和原来的节点组成 3- 节点即可。 -

+

如果是插入到 3- 节点上,就会产生一个临时 4- 节点时,需要将 4- 节点分裂成 3 个 2- 节点,并将中间的 2- 节点移到上层节点中。如果上移操作继续产生临时 4- 节点则一直进行分裂上移,直到不存在临时 4- 节点。 -

+

### 2. 性质 @@ -1220,7 +1224,7 @@ private void keys(Node x, Queue queue, Key lo, Key hi) { 2-3 查找树需要用到 2- 节点和 3- 节点,红黑树使用红链接来实现 3- 节点。指向一个节点的链接颜色如果为红色,那么这个节点和上层节点表示的是一个 3- 节点,而黑色则是普通链接。 -

+

红黑树具有以下性质: @@ -1263,9 +1267,7 @@ public class RedBlackBST, Value> { 因为合法的红链接都为左链接,如果出现右链接为红链接,那么就需要进行左旋转操作。 -

- -

+

```java public Node rotateLeft(Node h) { @@ -1284,9 +1286,7 @@ public Node rotateLeft(Node h) { 进行右旋转是为了转换两个连续的左红链接,这会在之后的插入过程中探讨。 -

- -

+

```java public Node rotateRight(Node h) { @@ -1304,9 +1304,7 @@ public Node rotateRight(Node h) { 一个 4- 节点在红黑树中表现为一个节点的左右子节点都是红色的。分裂 4- 节点除了需要将子节点的颜色由红变黑之外,同时需要将父节点的颜色由黑变红,从 2-3 树的角度看就是将中间节点移到上层节点。 -

- -

+

```java void flipColors(Node h){ @@ -1324,7 +1322,7 @@ void flipColors(Node h){ - 如果左子节点是红色的,而且左子节点的左子节点也是红色的,进行右旋转; - 如果左右子节点均为红色的,进行颜色转换。 -

+

```java public void put(Key key, Value val) { @@ -1352,23 +1350,7 @@ private Node put(Node x, Key key, Value val) { 根节点一定为黑色,因为根节点没有上层节点,也就没有上层节点的左链接指向根节点。flipColors() 有可能会使得根节点的颜色变为红色,每当根节点由红色变成黑色时树的黑链接高度加 1. -### 5. 删除最小键 - -如果最小键在一个 2- 节点中,那么删除该键会留下一个空链接,就破坏了平衡性,因此要确保最小键不在 2- 节点中。 - -将 2- 节点转换成 3- 节点或者 4- 节点有两种方法,一种是向上层节点拿一个 key,一种是向兄弟节点拿一个 key。如果上层节点是 2- 节点,那么就没办法从上层节点拿 key 了,因此要保证删除路径上的所有节点都不是 2- 节点。在向下删除的过程中,保证以下情况之一发生: - -1. 如果当前节点的左子节点不是 2- 节点,完成; -2. 如果当前节点的左子节点是 2- 节点而它的兄弟节点不是 2- 节点,向兄弟节点拿一个 key 过来; -3. 如果当前节点的左子节点和它的兄弟节点都是 2- 节点,将左子节点、父节点中的最小键和最近的兄弟节点合并为一个 4- 节点。 - -

- -最后得到一个含有最小键的 3- 节点或者 4- 节点,直接从中删除。然后再从头分解所有临时的 4- 节点。 - -

- -### 6. 分析 +### 5. 分析 一颗大小为 N 的红黑树的高度不会超过 2logN。最坏的情况下是它所对应的 2-3 树,构成最左边的路径节点全部都是 3- 节点而其余都是 2- 节点。 @@ -1442,7 +1424,7 @@ public class Transaction{ 拉链法使用链表来存储 hash 值相同的键,从而解决冲突。此时查找需要分两步,首先查找 Key 所在的链表,然后在链表中顺序查找。 -

+

对于 N 个键,M 条链表 (N>M),如果哈希函数能够满足均匀性的条件,每条链表的大小趋向于 N/M,因此未命中的查找和插入操作所需要的比较次数为 \~N/M。 @@ -1450,7 +1432,7 @@ public class Transaction{ 线性探测法使用空位来解决冲突,当冲突发生时,向前探测一个空位来存储冲突的键。使用线程探测法,数组的大小 M 应当大于键的个数 N(M>N)。 -

+

```java public class LinearProbingHashST { @@ -1567,7 +1549,7 @@ private void resize(int cap) { } ``` -虽然每次重新调整数组都需要重新把每个键值对插入到散列表,但是从摊还分析的角度来看,所需要的代价却是很小的。从下图可以看出,每次数组长度加倍后,累计平均值都会增加 1,因为表中每个键都需要重新计算散列值,但是随后平均值会下降。 +虽然每次重新调整数组都需要重新把每个键值对插入到散列表,但是从摊还分析的角度来看,所需要的代价却是很小的。从下图可以看出,每次数组长度加倍后,累计平均值都会增加 1,这是因为散列表中每个键都需要重新计算散列值。随后平均值会下降。

@@ -1575,7 +1557,13 @@ private void resize(int cap) { ### 1. 各种符号表实现的比较 -

+| 算法 | 插入 | 查找 | 是否有序 | +| :---: | :---: | :---: | :---: | +| 二分查找实现的有序表 | logN | N | yes | +| 二叉查找树 | logN | logN | yes | +| 2-3 查找树 | logN | logN | yes | +| 拉链法实现的散列表 | logN | N/M | no | +| 线性探测法试下的删列表 | logN | 1 | no | 应当优先考虑散列表,当需要有序性操作时使用红黑树。 @@ -1619,3 +1607,7 @@ public class SparseVector { } ``` +# 参考资料 + +- Sedgewick, Robert, and Kevin Wayne. _Algorithms_. Addison-Wesley Professional, 2011. + diff --git a/notes/计算机操作系统.md b/notes/计算机操作系统.md index 5ac1d886..86f82e10 100644 --- a/notes/计算机操作系统.md +++ b/notes/计算机操作系统.md @@ -106,7 +106,7 @@ Linux 的系统调用主要有以下这些: ### 2. 微内核 -由于操作系统不断复杂,为了实现因此将一部分操作系统功能移出内核,从而降低内核的复杂性。移出的部分根据分层的原则划分成若干服务,相互独立。 +由于操作系统不断复杂,因此将一部分操作系统功能移出内核,从而降低内核的复杂性。移出的部分根据分层的原则划分成若干服务,相互独立。 在微内核结构下,操作系统被划分成小的、定义良好的模块,只有微内核这一个模块运行在内核态,其余模块运行在用户态。 @@ -434,7 +434,7 @@ void writer() {

-五个哲学家围着一张圆桌,每个哲学家面前放着食物。哲学家的生活有两种交替活动:吃饭以及思考。当一个哲学家吃饭时,需要先拿起筷子左右的两根筷子,并且一次只能拿起一根筷子。 +五个哲学家围着一张圆桌,每个哲学家面前放着食物。哲学家的生活有两种交替活动:吃饭以及思考。当一个哲学家吃饭时,需要先拿起自己左右两边的两根筷子,并且一次只能拿起一根筷子。 下面是一种错误的解法,考虑到如果所有哲学家同时拿起左手边的筷子,那么就无法拿起右手边的筷子,造成死锁。 @@ -455,7 +455,7 @@ void philosopher(int i) { 为了防止死锁的发生,可以设置两个条件: -1. 必须同时拿起左右两个筷子; +1. 必须同时拿起左右两根筷子; 2. 只有在两个邻居都没有进餐的情况下才允许进餐。 ```c @@ -858,7 +858,7 @@ gcc -o hello hello.c 静态连接器以一组可重定向目标文件为输入,生成一个完全链接的可执行目标文件作为输出。链接器主要完成以下两个任务: -1. 符号解析:每个符号对应于一个函数、一个全局变量或一个静态变量,符号解析的目的是将每个符号引用于一个符号定义关联起来。 +1. 符号解析:每个符号对应于一个函数、一个全局变量或一个静态变量,符号解析的目的是将每个符号引用与一个符号定义关联起来。 2. 重定位:编译器和汇编器生成从地址 0 开始的代码和数据节,链接器通过把每个符号定义与一个内存位置关联起来,从而重定位这些节,然后修改所有对这些符号的引用,使得它们指向这个内存位置。

diff --git a/notes/计算机网络.md b/notes/计算机网络.md index d99e12b8..e21ecee6 100644 --- a/notes/计算机网络.md +++ b/notes/计算机网络.md @@ -6,22 +6,23 @@ * [主机之间的通信方式](#主机之间的通信方式) * [电路交换与分组交换](#电路交换与分组交换) * [时延](#时延) - * [计算机网络体系结构 *](#计算机网络体系结构-) + * [计算机网络体系结构*](#计算机网络体系结构) * [二、物理层](#二物理层) * [通信方式](#通信方式) * [带通调制](#带通调制) * [信道复用技术](#信道复用技术) * [三、数据链路层](#三数据链路层) + * [信道分类](#信道分类) * [三个基本问题](#三个基本问题) - * [点对点信道 - PPP 协议](#点对点信道---ppp-协议) - * [局域网的拓扑](#局域网的拓扑) - * [广播信道- CSMA/CD 协议 *](#广播信道--csmacd-协议-) - * [扩展局域网 *](#扩展局域网-) - * [MAC 层 *](#mac-层-) -* [四、网络层 *](#四网络层-) + * [局域网](#局域网) + * [PPP 协议](#ppp-协议) + * [CSMA/CD 协议*](#csmacd-协议) + * [扩展局域网*](#扩展局域网) + * [MAC 层*](#mac-层) +* [四、网络层*](#四网络层) * [网际协议 IP 概述](#网际协议-ip-概述) * [IP 数据报格式](#ip-数据报格式) - * [IP 地址编址](#ip-地址编址) + * [IP 地址编址方式](#ip-地址编址方式) * [IP 地址和 MAC 地址](#ip-地址和-mac-地址) * [地址解析协议 ARP](#地址解析协议-arp) * [路由器的结构](#路由器的结构) @@ -29,10 +30,9 @@ * [路由选择协议](#路由选择协议) * [网际控制报文协议 ICMP](#网际控制报文协议-icmp) * [分组网间探测 PING](#分组网间探测-ping) - * [IP 多播](#ip-多播) * [虚拟专用网 VPN](#虚拟专用网-vpn) * [网络地址转换 NAT](#网络地址转换-nat) -* [五、运输层 *](#五运输层-) +* [五、运输层*](#五运输层) * [UDP 和 TCP 的特点](#udp-和-tcp-的特点) * [UDP 首部格式](#udp-首部格式) * [TCP 首部格式](#tcp-首部格式) @@ -42,11 +42,10 @@ * [TCP 可靠传输](#tcp-可靠传输) * [TCP 流量控制](#tcp-流量控制) * [TCP 拥塞控制](#tcp-拥塞控制) -* [六、应用层 *](#六应用层-) +* [六、应用层*](#六应用层) * [域名系统 DNS](#域名系统-dns) * [文件传输协议 FTP](#文件传输协议-ftp) * [远程终端协议 TELNET](#远程终端协议-telnet) - * [万维网 WWW](#万维网-www) * [电子邮件协议](#电子邮件协议) * [动态主机配置协议 DHCP](#动态主机配置协议-dhcp) * [点对点传输 P2P](#点对点传输-p2p) @@ -62,7 +61,7 @@ 网络把主机连接起来,而互联网是把多种不同的网络连接起来,因此互联网是网络的网络。 -

+

## ISP @@ -72,7 +71,7 @@ 互联网交换点 IXP 允许两个 ISP 直接相连而不用经过第三个 ISP。 -

+

## 互联网的组成 @@ -80,7 +79,7 @@ 2. 核心部分:由大量的网络和连接这些网络的路由器组成,为边缘部分的主机提供服务。 -

+

## 主机之间的通信方式 @@ -90,7 +89,7 @@ ## 电路交换与分组交换 -

+

### 1. 电路交换 @@ -104,9 +103,9 @@ 分组交换也使用了存储转发,但是转发的是分组而不是报文。把整块数据称为一个报文,由于一个报文可能很长,需要先进行切分,来满足分组能处理的大小。在每个切分的数据前面加上首部之后就成为了分组,首部包含了目的地址和源地址等控制信息。 -

+

-存储转发允许在一条传输线路上传送多个主机的分组,因此两个用户之间的通信不需要占用端到端的线路资源。 +存储转发允许在一条传输线路上传送多个主机的分组,也就是说两个用户之间的通信不需要占用端到端的线路资源。 相比于报文交换,由于分组比报文更小,因此分组交换的存储转发速度更加快速。 @@ -114,7 +113,7 @@ 总时延 = 发送时延 + 传播时延 + 处理时延 + 排队时延 -

+

### 1. 发送时延 @@ -134,15 +133,15 @@ ### 3. 处理时延 -主机或路由器收到分组时进行处理所需要的时间,例如分析首部,从分组中提取数据部分等。 +主机或路由器收到分组时进行处理所需要的时间,例如分析首部、从分组中提取数据部、进行差错检验或查找适当的路由等。 ### 4. 排队时延 分组在路由器的输入队列和输出队列中排队等待的时间,取决于网络当前的通信量。 -## 计算机网络体系结构 * +## 计算机网络体系结构* -

+

### 1. 七层协议 @@ -157,9 +156,9 @@ 2. 运输层:提供的是进程间的通用数据传输服务。由于应用层协议很多,定义通用的运输层协议就可以支持不断增多的应用层协议。运输层包括两种协议:传输控制协议 TCP,提供面向连接、可靠的数据传输服务,数据单位为报文段;用户数据报协议 UDP,提供无连接、尽最大努力的数据传输服务,数据单位为用户数据报。TCP 主要提供完整性服务,UDP 主要提供及时性服务。 -3. 网络层:为主机之间提供数据传输服务,而像运输层协议那样是为主机中的进程提供服务。网络层把运输层传递下来的报文段或者用户数据报封装成分组。 +3. 网络层:为主机之间提供数据传输服务,而运输层协议是为主机中的进程提供服务。网络层把运输层传递下来的报文段或者用户数据报封装成分组。 -4. 数据链路层:网络层针对的还是主机之间的数据传输服务,而主机之间可以有很多链路,链路层协议就是为相邻结点之间提供服务。数据链路层把网络层传来的分组封装成帧。 +4. 数据链路层:网络层针对的还是主机之间的数据传输服务,而主机之间可以有很多链路,链路层协议就是为同一链路的结点提供服务。数据链路层把网络层传来的分组封装成帧。 5. 物理层:考虑的是怎样在传输媒体上传输数据比特流,而不是指具体的传输媒体。物理层的作用是尽可能屏蔽传输媒体和通信手段的差异,使数据链路层感觉不到这些差异。 @@ -169,7 +168,7 @@ 路由器只有下面三层协议,因为路由器位于网络核心中,不需要为进程或者应用程序提供服务,因此也就不需要运输层和应用层。 -

+

### 4. TCP/IP 体系结构 @@ -177,11 +176,11 @@ 现在的 TCP/IP 体系结构不严格遵循 OSI 分层概念,应用层可能会直接使用 IP 层或者网络接口层。 -

+

TCP/IP 协议族是一种沙漏形状,中间小两边大,IP 协议在其中占用举足轻重的地位。 -

+

# 二、物理层 @@ -195,7 +194,7 @@ TCP/IP 协议族是一种沙漏形状,中间小两边大,IP 协议在其中 模拟信号是连续的信号,数字信号是离散的信号。带通调制把数字信号转换为模拟信号。 -

+

## 信道复用技术 @@ -203,21 +202,21 @@ TCP/IP 协议族是一种沙漏形状,中间小两边大,IP 协议在其中 频分复用的所有用户在相同的时间占用不同的频率带宽资源;时分复用的所有用户在不同的时间占用相同的频率带宽资源。 -使用这两种方式进行通信,在通信的过程中用户会一直占用一部分信道资源。但是由于计算机数据的突发性质,没必要一直占用信道资源而不让出给其它用户使用,因此这两种方式对信道的利用率都不高。 +使用这两种方式进行通信,在通信的过程中用户会一直占用一部分信道资源。但是由于计算机数据的突发性质,通信过程没必要一直占用信道资源而不让出给其它用户使用,因此这两种方式对信道的利用率都不高。 -

+

### 2. 统计时分复用 是对时分复用的一种改进,不固定每个用户在时分复用帧中的位置,只要有数据就集中起来组成统计时分复用帧然后发送。 -

+

### 3. 波分复用 光的频分复用。由于光的频率很高,因此习惯上用波长而不是频率来表示所使用的光载波。 -

+

### 4. 码分复用 @@ -225,7 +224,7 @@ TCP/IP 协议族是一种沙漏形状,中间小两边大,IP 协议在其中

-为了方便,取 m=8,设码片 为 00011011。在拥有该码片的用户发送比特 1 时就发送该码片,发送比特 0 时就发送该码片的反码 11100100。 +为了讨论方便,取 m=8,设码片 为 00011011。在拥有该码片的用户发送比特 1 时就发送该码片,发送比特 0 时就发送该码片的反码 11100100。 在计算时将 00011011 记作 (-1 -1 -1 +1 +1 -1 +1 +1),可以得到 @@ -239,68 +238,75 @@ TCP/IP 协议族是一种沙漏形状,中间小两边大,IP 协议在其中 码分复用需要发送的数据量为原先的 m 倍。 -

+

# 三、数据链路层 +## 信道分类 + +1. 点对点信道:一对一通信方式; +2. 广播信道:一对多通信方式。 + ## 三个基本问题 ### 1. 封装成帧 将网络层传下来的分组添加首部和尾部,用于标记帧的开始和结束。 -

+

### 2. 透明传输 透明表示一个实际存在的事物看起来好像不存在一样。 -帧使用首部和尾部进行定界,如果帧的数据部分含有和首部尾部相同的内容,那么帧的开始和结束位置就会被错误的判定。需要在数据部分出现首部尾部相同的内容前面插入转义字符,如果出现转移字符,那么就在转义字符前面再加个转义字符,在接收端进行处理之后可以还原出原始数据。这个过程透明传输的内容是转义字符,用户察觉不到转义字符的存在。 +帧使用首部和尾部进行定界,如果帧的数据部分含有和首部尾部相同的内容,那么帧的开始和结束位置就会被错误的判定。需要在数据部分出现首部尾部相同的内容前面插入转义字符,如果出现转义字符,那么就在转义字符前面再加个转义字符,在接收端进行处理之后可以还原出原始数据。这个过程透明传输的内容是转义字符,用户察觉不到转义字符的存在。 -

+

### 3. 差错检测 目前数据链路层广泛使用了循环冗余检验(CRC)来检查比特差错。 -## 点对点信道 - PPP 协议 +## 局域网 -互联网用户通常需要连接到某个 ISP 之后才能接入到互联网,PPP 协议就是用户计算机和 ISP 进行通信时所使用的数据链路层协议。 +局域网是典型的一种广播信道,主要特点是网络为一个单位所拥有,且地理范围和站点数目均有限。 -

+可以按照网络拓扑对局域网进行分类: -在 PPP 的帧中 +

+ +## PPP 协议 + +用于点对点信道中。互联网用户通常需要连接到某个 ISP 之后才能接入到互联网,PPP 协议是用户计算机和 ISP 进行通信时所使用的数据链路层协议。 + +

+ +在 PPP 的帧中: - F 字段为帧的定界符 - A 和 C 字段暂时没有意义 - FCS 字段是使用 CRC 的检验序列 - 信息部分的长度不超过 1500 -

+

-## 局域网的拓扑 +## CSMA/CD 协议* -

- -## 广播信道- CSMA/CD 协议 * - -在广播信道上,同一时间只能允许一台计算机发送数据。 +用于广播信道中。在广播信道上,同一时间只能允许一台计算机发送数据。 CSMA/CD 表示载波监听多点接入 / 碰撞检测。 - **多点接入** :说明这是总线型网络,许多计算机以多点的方式连接到总线上。 +- **载波监听** :每个站都必须不停地监听信道。在发送前,如果监听到信道正在使用,就必须等待。 +- **碰撞检测** :在发送中,如果监听到信道已有其它站正在发送数据,就表示发生了碰撞。虽然每一个站在发送数据之前都已经监听到信道为空闲,但是由于电磁波的传播时延的存在,还是有可能会发生碰撞。 -- **载波监听** :每个站都必须不停地监听信道。在发送前,如果检听信道正在使用,就必须等待。 - -- **碰撞检测** :在发送中,如果监听 到信道已有其它站正在发送数据,就表示发生了碰撞。虽然每一个站在发送数据之前都已经监听到信道为空闲,但是由于电磁波的传播时延的存在,还是有可能会发生碰撞。 - -

+

记端到端的传播时延为 τ,最先发送的站点最多经过 2τ 就可以知道是否发生了碰撞,称 2τ 为 **争用期** 。只有经过争用期之后还没有检测到碰撞,才能肯定这次发送不会发生碰撞。 当发生碰撞时,站点要停止发送,等待一段时间再发送。这个时间采用 **截断二进制指数退避算法** 来确定,从离散的整数集合 {0, 1, .., (2k-1)} 中随机取出一个数,记作 r,然后取 r 倍的争用期作为重传等待时间。 -## 扩展局域网 * +## 扩展局域网* ### 1. 在物理层进行扩展 @@ -312,41 +318,40 @@ CSMA/CD 表示载波监听多点接入 / 碰撞检测。 集线器是一种共享式的传输设备,意味着同一时刻只能传输一组数据帧。 -

+

### 2. 在链路层进行扩展 -最开始使用的是网桥,它收到一个帧时,根据帧的 MAC 地址,查找网桥中的地址表,确定将帧转发的接口。 +最开始使用的是网桥,它收到一个帧时,根据帧的 MAC 地址,查找网桥中的地址表,确定帧转发的接口。 网桥不是共享式设备,因此性能比集线器这种共享式设备更高。 交换机的问世很快就淘汰了网桥,它实质上是一个多接口网桥,而网桥是两接口。交换机的每个接口都能直接与一个主机或者另一个交换机相连,并且一般都工作在全双工方式。 -交换机具有自学习能力,学习的是交换表的内容,交换表中存储着 MAC 地址到接口的映射。下图中,交换机有 4 个接口,主机 A 向主机 B 发送数据帧时,交换机把主机 A 到接口 1 的映射写入交换表中。为了发送数据帧到 B,先查交换表,此时没有主机 B 的表项,那么主机 A 就发送广播帧,主机 C 和主机 D 会丢弃该帧,主机 B 收下之后,查找交换表得到主机 A 映射的接口为 1,因此就把帧发送给主机 A,同时交换机添加主机 B 到接口 3 的映射。 +交换机具有自学习能力,学习的是交换表的内容。交换表中存储着 MAC 地址到接口的映射。下图中,交换机有 4 个接口,主机 A 向主机 B 发送数据帧时,交换机把主机 A 到接口 1 的映射写入交换表中。为了发送数据帧到 B,先查交换表,此时没有主机 B 的表项,那么主机 A 就发送广播帧,主机 C 和主机 D 会丢弃该帧。主机 B 收下之后,查找交换表得到主机 A 映射的接口为 1,就发送数据帧到接口 1,同时交换机添加主机 B 到接口 3 的映射。 -

+

### 3. 虚拟局域网 虚拟局域网可以建立与物理位置无关的逻辑组,只有在同一个虚拟局域网中的成员才会收到链路层广播信息,例如下图中 (A1, A2, A3, A4) 属于一个虚拟局域网,A1 发送的广播会被 A2、A3、A4 收到,而其它站点收不到。 -

+

-## MAC 层 * +## MAC 层* MAC 地址是 6 字节(48 位)的地址,用于唯一标识网络适配器(网卡),一台主机拥有多少个适配器就有多少个 MAC 地址,例如笔记本电脑普遍存在无线网络适配器和有线网络适配器。 -

+

+ +在 MAC 帧中: - **类型** :标记上层使用的协议; - - **数据** :长度在 46-1500 之间,如果太小则需要填充; - - **FCS** :帧检验序列,使用的是 CRC 检验方法; - - **前同步码** :只是为了计算 FCS 临时加入的,计算结束之后会丢弃。 -# 四、网络层 * +# 四、网络层* ## 网际协议 IP 概述 @@ -354,7 +359,7 @@ MAC 地址是 6 字节(48 位)的地址,用于唯一标识网络适配器 使用 IP 协议,可以把异构的物理网络连接起来,使得在网络层看起来好像是一个统一的网络。 -

+

与 IP 协议配套使用的还有三个协议: @@ -362,15 +367,15 @@ MAC 地址是 6 字节(48 位)的地址,用于唯一标识网络适配器 2. 网际控制报文协议 ICMP(Internet Control Message Protocol) 3. 网际组管理协议 IGMP(Internet Group Management Protocol) -

+

## IP 数据报格式 -

+

- **版本** : 有 4(IPv4)和 6(IPv6)两个值; -- **首部长度** : 占 4 位,因此最大值为 15。值为 1 表示的是 1 个 32 位字的长度,也就是 4 字节。因为首部固定长度为 20 字节,因此该值最小为 5。如果可选部分的长度不是 4 字节的整数倍,就用尾部的填充部分来填充。 +- **首部长度** : 占 4 位,因此最大值为 15。值为 1 表示的是 1 个 32 位字的长度,也就是 4 字节。因为首部固定长度为 20 字节,因此该值最小为 5。如果可选字段的长度不是 4 字节的整数倍,就用尾部的填充部分来填充。 - **区分服务** : 用来获得更好的服务,一般情况下不使用。 @@ -380,7 +385,7 @@ MAC 地址是 6 字节(48 位)的地址,用于唯一标识网络适配器 - **片偏移** : 和标识符一起,用于发生分片的情况。片偏移的单位为 8 字节。 -

+

- **生存时间** :TTL,它的存在是为了防止无法交付的数据报在互联网中不断兜圈子。以路由器跳数为单位,当 TTL 为 0 时就丢弃数据报。 @@ -388,21 +393,21 @@ MAC 地址是 6 字节(48 位)的地址,用于唯一标识网络适配器 - **首部检验和** :因为数据报每经过一个路由器,都要重新计算检验和,因此检验和不包含数据部分可以减少计算的工作量。 -## IP 地址编址 +## IP 地址编址方式 IP 地址的编址方式经历了三个历史阶段: -1. 分类; -2. 子网划分; -3. 无分类。 +1. 分类 +2. 子网划分 +3. 无分类 ### 1. 分类 -由两部分组成,网络号和主机号,其中不同类别具有不同的网络号长度,并且是固定的。 +由两部分组成,网络号和主机号,其中不同分类具有不同的网络号长度,并且是固定的。 IP 地址 ::= {< 网络号 >, < 主机号 >} -

+

### 2. 子网划分 @@ -430,48 +435,38 @@ CIDR 的地址掩码可以继续称为子网掩码,子网掩码首 1 长度为 网络层实现主机之间的通信,而链路层实现具体每段链路之间的通信。因此在通信过程中,IP 数据报的源地址和目的地址始终不变,而 MAC 地址随着链路的改变而改变。 -

+

## 地址解析协议 ARP 实现由 IP 地址得到 MAC 地址。 -

+

每个主机都有一个 ARP 高速缓存,里面有本局域网上的各主机和路由器的 IP 地址到硬件地址的映射表。 如果主机 A 知道主机 B 的 IP 地址,但是 ARP 高速缓存中没有该 IP 地址到 MAC 地址的映射,此时主机 A 通过广播的方式发送 ARP 请求分组,主机 B 收到该请求后会发送 ARP 响应分组给主机 A 告知其 MAC 地址,随后主机 A 向其高速缓存中写入主机 B 的 IP 地址到硬件地址的映射。 -

+

## 路由器的结构 -路由器从功能上可以划分为两大部分:路由选择和分组转发。 +路由器从功能上可以划分为:路由选择和分组转发。 -分组转发部分由三部分组成:交换结构、一组输入端口和一组输出端口。 - -

- -交换结构的交换网络有以下三种实现方式: - -

+分组转发结构由三个部分组成:交换结构、一组输入端口和一组输出端口。 +

## 路由器分组转发流程 -1. 从数据报的首部提取目的主机的 IP 地址 D,得到目的网络地址 N。(路由表项是网络号而不是 IP 地址,这样做大大减少了路由表条目数量); - +1. 从数据报的首部提取目的主机的 IP 地址 D,得到目的网络地址 N。 2. 若 N 就是与此路由器直接相连的某个网络地址,则进行直接交付; - 3. 若路由表中有目的地址为 D 的特定主机路由,则把数据报传送给表中所指明的下一跳路由器; - 4. 若路由表中有到达网络 N 的路由,则把数据报传送给路由表中所指明的下一跳路由器; - 5. 若路由表中有一个默认路由,则把数据报传送给路由表中所指明的默认路由器; - 6. 报告转发分组出错。 -

+

## 路由选择协议 @@ -484,7 +479,7 @@ CIDR 的地址掩码可以继续称为子网掩码,子网掩码首 1 长度为 1. 内部网关协议 IGP(Interior Gateway Protocol):在 AS 内部使用,如 RIP 和 OSPF。 2. 外部网关协议 EGP(External Gateway Protocol):在 AS 之间使用,如 BGP。 -

+

### 1. 内部网关协议 RIP @@ -524,32 +519,31 @@ BGP 只能寻找一条比较好的路由,而不是最佳路由。它采用路 每个 AS 都必须配置 BGP 发言人,通过在两个相邻 BGP 发言人之间建立 TCP 连接来交换路由信息。 -

+

## 网际控制报文协议 ICMP ICMP 是为了更有效地转发 IP 数据报和提高交付成功的机会。它封装在 IP 数据报中,但是不属于高层协议。 -

+

ICMP 报文分为差错报告报文和询问报文。 -

+

## 分组网间探测 PING PING 是 ICMP 的一个重要应用,主要用来测试两台主机之间的连通性。 -PING 的过程: +Ping 发送的 IP 数据报封装的是无法交付的 UDP 用户数据报。 -1. PING 同一个网段的主机:查找目的主机的 MAC 地址,然后直接交付。如果无法查找到 MAC 地址,就要进行一次 ARP 请求。 -2. PING 不同网段的主机:发送到网关让其进行转发。同样要发送到网关也需要通过查找网关的 MAC 地址,根据 MAC 地址进行转发。 +Ping 的过程: -## IP 多播 +1. 源主机向目的主机发送一连串的 IP 数据报。第一个数据报 P1 的生存时间 TTL 设置为 1,但 P1 到达路径上的第一个路由器 R1 时,R1 收下它并把 TTL 减 1,此时 TTL 等于 0,R1 就把 P1 丢弃,并向源主机发送一个 ICMP 时间超过差错报告报文; +2. 源主机接着发送第二个数据报 P2,并把 TTL 设置为 2。P2 先到达 R1,R1 收下后把 TTL 减 1 再转发给 R2,R2 收下后也把 TTL 减 1,由于此时 TTL 等于 0,R2 就丢弃 P2,并向源主机发送一个 ICMP 时间超过差错报文。 +3. 不断执行这样的步骤,直到最后一个数据报刚刚到达目的主机,主机不转发数据报,也不把 TTL 值减 1。但是因为数据报封装的是无法交付的 UDP,因此目的主机要向源主机发送 ICMP 终点不可达差错报告报文。 +4. 之后源主机知道了到达目的主机所经过的路由器 IP 地址以及到达每个路由器的往返时间。 -在一对多的通信中,多播不需要将分组复制多份,从而大大节约网络资源。 - -

## 虚拟专用网 VPN @@ -563,9 +557,9 @@ PING 的过程: VPN 使用公用的互联网作为本机构各专用网之间的通信载体。专用指机构内的主机只与本机构内的其它主机通信;虚拟指“好像是”,而实际上并不是,它有经过公用的互联网。 -下图中,场所 A 和 B 的通信部经过互联网,如果场所 A 的主机 X 要和另一个场所 B 的主机 Y 通信,IP 数据报的源地址是 10.1.0.1,目的地址是 10.2.0.3。数据报先发送到与互联网相连的路由器 R1,R1 对内部数据进行加密,然后重新加上数据报的首部,源地址是路由器 R1 的全球地址 125.1.2.3,目的地址是路由器 R2 的全球地址 194.4.5.6。路由器 R2 收到数据报后将数据部分进行解密,恢复原来的数据报,此时目的地址为 10.2.0.3,就交付给 Y。 +下图中,场所 A 和 B 的通信经过互联网,如果场所 A 的主机 X 要和另一个场所 B 的主机 Y 通信,IP 数据报的源地址是 10.1.0.1,目的地址是 10.2.0.3。数据报先发送到与互联网相连的路由器 R1,R1 对内部数据进行加密,然后重新加上数据报的首部,源地址是路由器 R1 的全球地址 125.1.2.3,目的地址是路由器 R2 的全球地址 194.4.5.6。路由器 R2 收到数据报后将数据部分进行解密,恢复原来的数据报,此时目的地址为 10.2.0.3,就交付给 Y。 -

+

## 网络地址转换 NAT @@ -573,29 +567,27 @@ VPN 使用公用的互联网作为本机构各专用网之间的通信载体。 在以前,NAT 将本地 IP 和全球 IP 一一对应,这种方式下拥有 n 个全球 IP 地址的专用网内最多只可以同时有 n 台主机接入互联网。为了更有效地利用全球 IP 地址,现在常用的 NAT 转换表把运输层的端口号也用上了,使得多个专用网内部的主机共用一个全球 IP 地址。使用端口号的 NAT 也叫做网络地址与端口转换 NAPT。 -

+

-# 五、运输层 * +# 五、运输层* -网络层只把分组发送到目的主机,但是真正通信的并不是主机而是主机中的进程。 - -运输层提供了应用进程间的逻辑通信。运输层向高层用户屏蔽了下面网络层的核心细节,使应用程序看见的好像在两个运输层实体之间有一条端到端的逻辑通信信道。 +网络层只把分组发送到目的主机,但是真正通信的并不是主机而是主机中的进程。运输层提供了进程间的逻辑通信,运输层向高层用户屏蔽了下面网络层的核心细节,使应用程序看见的好像在两个运输层实体之间有一条端到端的逻辑通信信道。 ## UDP 和 TCP 的特点 -- 用户数据包协议 UDP(User Datagram Protocol)是无连接的,尽最大可能交付,没有拥塞控制,面向报文(对于应用程序传下来的报文不合并也不拆分,只是添加 UDP 首部)。 +- 用户数据报协议 UDP(User Datagram Protocol)是无连接的,尽最大可能交付,没有拥塞控制,面向报文(对于应用程序传下来的报文不合并也不拆分,只是添加 UDP 首部)。 -- 传输控制协议 TCP(Transmission Control Protocol) 是面向连接的,提供可靠交付,有流量控制,拥塞控制,提供全双工通信,面向字节流(把应用层传下来的报文看成字节流,把字节流组织成大小不等的数据块) +- 传输控制协议 TCP(Transmission Control Protocol)是面向连接的,提供可靠交付,有流量控制,拥塞控制,提供全双工通信,面向字节流(把应用层传下来的报文看成字节流,把字节流组织成大小不等的数据块)。 ## UDP 首部格式 -

+

首部字段只有 8 个字节,包括源端口、目的端口、长度、检验和。12 字节的伪首部是为了计算检验和临时添加的。 ## TCP 首部格式 -

+

- **序号** :用于对字节流进行编号,例如序号为 301,表示第一个字节的编号为 301,如果携带的数据长度为 100 字节,那么下一个报文段的序号应为 401。 @@ -613,7 +605,7 @@ VPN 使用公用的互联网作为本机构各专用网之间的通信载体。 ## TCP 的三次握手 -

+

假设 A 为客户端,B 为服务器端。 @@ -624,34 +616,46 @@ VPN 使用公用的互联网作为本机构各专用网之间的通信载体。 3. B 收到连接请求报文段,如果同意建立连接,则向 A 发送连接确认报文段,SYN=1,ACK=1,确认号为 x+1,同时也选择一个初始的序号 y。 4. A 收到 B 的连接确认报文段后,还要向 B 发出确认,确认号为 y+1,序号为 x+1。 + 5. B 收到 A 的确认后,连接建立。 +**三次握手的原因** + +第三次握手是为了防止失效的连接请求到达服务器,让服务器错误打开连接。 + +失效的连接请求是指,客户端发送的连接请求在网络中滞留,客户端因为没及时收到服务器端发送的连接确认,因此就重新发送了连接请求。滞留的连接请求并不是丢失,之后还是会到达服务器。如果不进行第三次握手,那么服务器会误认为客户端重新请求连接,然后打开了连接。但是并不是客户端真正打开这个连接,因此客户端不会给服务器发送数据,这个连接就白白浪费了。 ## TCP 的四次挥手 -

+

以下描述不讨论序号和确认号,因为序号和确认号的规则比较简单。并且不讨论 ACK,因为 ACK 在连接建立之后都为 1。 -1. A 发送连接释放报文段,FIN=1; +1. A 发送连接释放报文段,FIN=1。 -2. B 收到之后发出确认,此时 TCP 属于半关闭状态,B 能向 A 发送数据但是 A 不能向 B 发送数据; +2. B 收到之后发出确认,此时 TCP 属于半关闭状态,B 能向 A 发送数据但是 A 不能向 B 发送数据。 -3. 当 B 要不再需要连接时,发送连接释放请求报文段,FIN=1; +3. 当 B 要不再需要连接时,发送连接释放请求报文段,FIN=1。 -4. A 收到后发出确认,此时连接释放。 +4. A 收到后发出确认,进入 TIME-WAIT 状态,等待 2MSL 时间后释放连接。 -### TIME_WAIT +5. B 收到 A 的确认后释放连接。 -客户端接收到服务器端的 FIN 报文后进入此状态,此时并不是直接进入 CLOSED 状态,还需要等待一个时间计时器设置的时间。这么做有两个理由: +**四次挥手的原因** + +客户端发送了 FIN 连接释放报文之后,服务器收到了这个报文,就进入了 CLOSE-WAIT 状态。这个状态是为了让服务器端发送还未传送完毕的数据,传送完毕之后,服务器会发送 FIN 连接释放报文。 + +**TIME_WAIT** + +客户端接收到服务器端的 FIN 报文后进入此状态,此时并不是直接进入 CLOSED 状态,还需要等待一个时间计时器设置的时间 2MSL。这么做有两个理由: 1. 确保最后一个确认报文段能够到达。如果 B 没收到 A 发送来的确认报文段,那么就会重新发送连接释放请求报文段,A 等待一段时间就是为了处理这种情况的发生。 -2. 可能存在“已失效的连接请求报文段”,为了防止这种报文段出现在本次连接之外,需要等待一段时间。 +2. 等待一段时间是为了让本连接持续时间内所产生的所有报文段都从网络中消失,使得下一个新的连接不会出现旧的连接请求报文段。 ## TCP 滑动窗口 -

+

窗口是缓存的一部分,用来暂时存放字节流。发送方和接收方各有一个窗口,接收方通过 TCP 报文段中的窗口字段告诉发送方自己的窗口大小,发送方根据这个值和其它信息设置自己的窗口大小。 @@ -677,30 +681,30 @@ TCP 使用超时重传来实现可靠传输:如果一个已经发送的报文 流量控制是为了控制发送方发送速率,保证接收方来得及接收。 -接收方发送的确认报文中的窗口字段可以用来控制发送方窗口大小,从而影响发送方的发送速率。例如将窗口字段设置为 0,则发送方不能发送数据。 +接收方发送的确认报文中的窗口字段可以用来控制发送方窗口大小,从而影响发送方的发送速率。将窗口字段设置为 0,则发送方不能发送数据。 ## TCP 拥塞控制 如果网络出现拥塞,分组将会丢失,此时发送方会继续重传,从而导致网络拥塞程度更高。因此当出现拥塞时,应当控制发送方的速率。这一点和流量控制很像,但是出发点不同。流量控制是为了让接收方能来得及接受,而拥塞控制是为了降低整个网络的拥塞程度。 -

+

-TCP 主要通过四种算法来进行拥塞控制:慢开始、拥塞避免、快重传、快恢复。发送方需要维护有一个叫做拥塞窗口(cwnd)的状态变量。注意拥塞窗口与发送方窗口的区别,拥塞窗口只是一个状态变量,实际决定发送方能发送多少数据的是发送方窗口。 +TCP 主要通过四种算法来进行拥塞控制:慢开始、拥塞避免、快重传、快恢复。发送方需要维护一个叫做拥塞窗口(cwnd)的状态变量。注意拥塞窗口与发送方窗口的区别,拥塞窗口只是一个状态变量,实际决定发送方能发送多少数据的是发送方窗口。 为了便于讨论,做如下假设: 1. 接收方有足够大的接收缓存,因此不会发生流量控制; 2. 虽然 TCP 的窗口基于字节,但是这里设窗口的大小单位为报文段。 -

+

### 1. 慢开始与拥塞避免 -发送的最初执行慢开始,令 cwnd=1,发送方只能发送 1 个报文段;当收到确认后,将 cwnd 加倍,因此之后发送方能够发送的报文段为:2、4、8 ... +发送的最初执行慢开始,令 cwnd=1,发送方只能发送 1 个报文段;当收到确认后,将 cwnd 加倍,因此之后发送方能够发送的报文段数量为:2、4、8 ... 注意到慢开始每个轮次都将 cwnd 加倍,这样会让 cwnd 增长速度非常快,从而使得发送方发送的速度增长速度过快,网络拥塞的可能也就更高。设置一个慢开始门限 ssthresh,当 cwnd >= ssthresh 时,进入拥塞避免,每个轮次只将 cwnd 加 1。 -如果出现了超时,则令 ssthresh = cwnd / 2,然后重新执行慢开始。 +如果出现了超时,则令 ssthresh = cwnd/2,然后重新执行慢开始。 ### 2. 快重传与快恢复 @@ -708,11 +712,11 @@ TCP 主要通过四种算法来进行拥塞控制:慢开始、拥塞避免、 在发送方,如果收到三个重复确认,那么可以确认下一个报文段丢失,例如收到三个 M2 ,则 M3 丢失。此时执行快重传,立即重传下一个报文段。 -在这种情况下,只是丢失个别报文段,而不是网络拥塞,因此执行快恢复,令 ssthresh = cwnd / 2 ,cwnd = ssthresh,注意到此时直接进入拥塞避免。 +在这种情况下,只是丢失个别报文段,而不是网络拥塞,因此执行快恢复,令 ssthresh = cwnd/2 ,cwnd = ssthresh,注意到此时直接进入拥塞避免。 -

+

-# 六、应用层 * +# 六、应用层* ## 域名系统 DNS @@ -724,9 +728,9 @@ TCP 主要通过四种算法来进行拥塞控制:慢开始、拥塞避免、 一个域名由多个层次构成,从上层到下层分别为顶级域名、二级域名、三级域名以及四级域名。所有域名可以画成一颗域名树。 -

+

-

+

域名服务器可以分为以下四类: @@ -737,25 +741,25 @@ TCP 主要通过四种算法来进行拥塞控制:慢开始、拥塞避免、 区和域的概念不同,可以在一个域中划分多个区。图 b 在域 abc.com 中划分了两个区:abc.com 和 y.abc.com -

+

因此就需要两个权限域名服务器: -

+

### 2. 解析过程 主机向本地域名服务器解析的过程采用递归,而本地域名服务器向其它域名服务器解析可以使用递归和迭代两种方式。 -迭代的方式下,本地域名服务器向一个域名服务器解析请求解析之后,结果返回到本地域名服务器,然后本地域名服务器继续向其它域名服务器请求解析;而递归地方式下,结果不是直接返回的,而是继续向前请求解析,最后的结果才会返回。 +迭代的方式下,本地域名服务器向一个域名服务器解析请求解析之后,结果返回到本地域名服务器,然后本地域名服务器继续向其它域名服务器请求解析;而递归的方式下,结果不是直接返回的,而是继续向前请求解析,最后的结果才会返回。 -

+

## 文件传输协议 FTP FTP 在运输层使用 TCP,并且需要建立两个并行的 TCP 连接:控制连接和数据连接。控制连接在整个会话期间一直保持打开,而数据连接在数据传送完毕之后就关闭。控制连接使用端口号 21,数据连接使用端口号 20。 -

+

## 远程终端协议 TELNET @@ -763,15 +767,11 @@ TELNET 用于登录到远程主机上,并且远程主机上的输出也会返 TELNET 可以适应许多计算机和操作系统的差异,例如不同操作系统系统的换行符定义。 -## 万维网 WWW - -[HTTP](https://github.com/CyC2018/InterviewNotes/blob/master/notes/HTTP.md) - ## 电子邮件协议 一个电子邮件系统由三部分组成:用户代理、邮件服务器以及邮件发送协议和读取协议。其中发送协议常用 SMTP,读取协议常用 POP3 和 IMAP。 -

+

### 1. POP3 @@ -785,7 +785,7 @@ IMAP 协议中客户端和服务器上的邮件保持同步,如果不去手动 SMTP 只能发送 ASCII 码,而互联网邮件扩充 MIME 可以发送二进制文件。MIME 并没有改动或者取代 SMTP,而是增加邮件主题的结构,定义了非 ASCII 码的编码规则。 -

+

## 动态主机配置协议 DHCP @@ -884,5 +884,5 @@ P2P 是一个分布式系统,任何时候都有对等方加入或者退出。 # 参考资料 -- 计算机网络 第七版 -- 计算机网络 自顶向下方法 +- 计算机网络, 谢希仁 +- JamesF.Kurose, KeithW.Ross, 库罗斯, 等. 计算机网络: 自顶向下方法 [M]. 机械工业出版社, 2014. diff --git a/notes/设计模式.md b/notes/设计模式.md index 13815198..7df60ac7 100644 --- a/notes/设计模式.md +++ b/notes/设计模式.md @@ -1,23 +1,11 @@ * [一、前言](#一前言) * [二、设计模式概念](#二设计模式概念) -* [三、策略模式](#三策略模式) -* [三、观察者模式](#三观察者模式) -* [四、装饰模式](#四装饰模式) -* [五、简单工厂](#五简单工厂) -* [六、工厂方法模式](#六工厂方法模式) -* [七、抽象工厂模式](#七抽象工厂模式) -* [八、单例模式](#八单例模式) -* [九、命令模式](#九命令模式) -* [十、适配器模式](#十适配器模式) -* [十、外观模式](#十外观模式) -* [十一、模板方法模式](#十一模板方法模式) -* [十二、迭代器模式](#十二迭代器模式) -* [十三、组合模式](#十三组合模式) -* [十四、状态模式](#十四状态模式) -* [十五、代理模式](#十五代理模式) -* [十六、MVC](#十六mvc) -* [十七、与设计模式相处](#十七与设计模式相处) +* [三、单例模式](#三单例模式) +* [四、简单工厂](#四简单工厂) +* [五、工厂方法模式](#五工厂方法模式) +* [六、抽象工厂模式](#六抽象工厂模式) +* [参考资料](#参考资料) @@ -25,742 +13,46 @@ 文中涉及一些 UML 类图,为了更好地理解,可以先阅读 [UML 类图](https://github.com/CyC2018/Interview-Notebook/blob/master/notes/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E6%80%9D%E6%83%B3.md#%E7%AC%AC%E4%B8%89%E7%AB%A0-uml)。 -需要说明的一点是,文中的 UML 类图和规范的 UML 类图不大相同,其中组合关系使用以下箭头表示: - -

- # 二、设计模式概念 设计模式不是代码,而是解决问题的方案,学习现有的设计模式可以做到经验复用。 拥有设计模式词汇,在沟通时就能用更少的词汇来讨论,并且不需要了解底层细节。 -# 三、策略模式 +# 三、单例模式 -## 模式定义 - -定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。 - -## 问题描述 - -设计不同种类的鸭子拥有不同的叫声和飞行方式。 - -## 简单实现方案 - -使用继承的解决方案如下,这种方案代码无法复用,如果两个鸭子类拥有同样的飞行方式,就有两份重复的代码。 - -

- -## 设计原则 - -**封装变化** :在这里变化的是鸭子叫和飞行的行为方式。 - -**针对接口编程,而不是针对实现编程** :变量声明的类型为父类,而不是具体的某个子类。父类中的方法实现不在父类,而是在各个子类。程序在运行时可以动态改变变量所指向的子类类型。 - -运用这一原则,将叫和飞行的行为抽象出来,实现多种不同的叫和飞行的子类,让子类去实现具体的叫和飞行方式。 - -

- -**多用组合,少用继承** :组合也就是 HAS-A 关系,通过组合,可以在运行时动态改变实现,只要通过改变父类对象具体指向哪个子类即可。而继承就不能做到这些,继承体系在创建类时就已经确定。 - -运用这一原则,在 Duck 类中组合 FlyBehavior 和 QuackBehavior 类,performQuack() 和 performFly() 方法委托给这两个类去处理。通过这种方式,一个 Duck 子类可以根据需要去初始化 FlyBehavior 和 QuackBehavior 的子类对象,并且也可以动态地进行改变。 - -

- -## 问题的解决方案类图 - -(可放大网页查看) - -

- -## 代码实现 - -```java -public abstract class Duck { - FlyBehavior flyBehavior; - QuackBehavior quackBehavior; - - public Duck(){ - } - - public void performFly(){ - flyBehavior.fly(); - } - - public void setFlyBehavior(FlyBehavior fb){ - flyBehavior = fb; - } - - public void performQuack(){ - quackBehavior.quack(); - } - - public void setQuackBehavior(QuackBehavior qb){ - quackBehavior = qb; - } -} -``` -```java -public class MallardDuck extends Duck{ - public MallardDuck(){ - flyBehavior = new FlyWithWings(); - quackBehavior = new Quack(); - } -} -``` -```java -public interface FlyBehavior { - void fly(); -} -``` -```java -public class FlyNoWay implements FlyBehavior{ - @Override - public void fly() { - System.out.println("FlyBehavior.FlyNoWay"); - } -} -``` -```java -public class FlyWithWings implements FlyBehavior{ - @Override - public void fly() { - System.out.println("FlyBehavior.FlyWithWings"); - } -} -``` -```java -public interface QuackBehavior { - void quack(); -} -``` -```java -public class Quack implements QuackBehavior{ - @Override - public void quack() { - System.out.println("QuackBehavior.Quack"); - } -} -``` -```java -public class MuteQuack implements QuackBehavior{ - @Override - public void quack() { - System.out.println("QuackBehavior.MuteQuack"); - } -} -``` -```java -public class Squeak implements QuackBehavior{ - @Override - public void quack() { - System.out.println("QuackBehavior.Squeak"); - } -} -``` -```java -public class MiniDuckSimulator { - public static void main(String[] args) { - Duck mallardDuck = new MallardDuck(); - mallardDuck.performQuack(); - mallardDuck.performFly(); - mallardDuck.setFlyBehavior(new FlyNoWay()); - mallardDuck.performFly(); - } -} -``` -执行结果 -```html -QuackBehavior.Quack -FlyBehavior.FlyWithWings -FlyBehavior.FlyNoWay -``` - -# 三、观察者模式 - -## 模式定义 - -定义了对象之间的一对多依赖,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。主题(Subject)是被观察的对象,而其所有依赖者(Observer)称为观察者。 - -

- -## 模式类图 - -主题具有注册和移除观察者、并通知所有注册者的功能,主题是通过维护一张观察者列表来实现这些操作的。 - -观察者拥有一个主题对象的引用,因为注册、移除观察者功能,还有数据都在主题当中,必须通过操作主题才能完成相应操作。 - -

- -## 问题描述 - -天气数据布告板会在天气信息发生改变时更新其内容,布告板有多个,并且在将来会继续增加。 - -## 问题的解决方案类图 - -

- -## 设计原则 - -为交互对象之间的松耦合设计而努力:当两个对象之间松耦合,它们依然可以交互,但是不清楚彼此的细节。由于松耦合的两个对象之间互相依赖程度很低,因此系统具有弹性,能够应对变化。 - -## 代码实现 - -```java -public interface Subject { - public void resisterObserver(Observer o); - public void removeObserver(Observer o); - public void notifyObserver(); -} -``` -```java - -public class WeatherData implements Subject { - private List observers; - private float temperature; - private float humidity; - private float pressure; - - public WeatherData() { - observers = new ArrayList<>(); - } - - @Override - public void resisterObserver(Observer o) { - observers.add(o); - } - - @Override - public void removeObserver(Observer o) { - int i = observers.indexOf(o); - if (i >= 0) { - observers.remove(i); - } - } - - @Override - public void notifyObserver() { - for (Observer o : observers) { - o.update(temperature, humidity, pressure); - } - } - - public void setMeasurements(float temperature, float humidity, float pressure) { - this.temperature = temperature; - this.humidity = humidity; - this.pressure = pressure; - notifyObserver(); - } -} -``` -```java -public interface Observer { - public void update(float temp, float humidity, float pressure); -} -``` -```java -public class CurrentConditionsDisplay implements Observer { - private Subject weatherData; - - public CurrentConditionsDisplay(Subject weatherData) { - this.weatherData = weatherData; - weatherData.resisterObserver(this); - } - - @Override - public void update(float temp, float humidity, float pressure) { - System.out.println("CurrentConditionsDisplay.update:" + temp + " " + humidity + " " + pressure); - } -} -``` -```java -public class StatisticsDisplay implements Observer { - private Subject weatherData; - - public StatisticsDisplay(Subject weatherData) { - this.weatherData = weatherData; - weatherData.resisterObserver(this); - } - - @Override - public void update(float temp, float humidity, float pressure) { - System.out.println("StatisticsDisplay.update:" + temp + " " + humidity + " " + pressure); - } -} -``` -```java -public class WeatherStation { - public static void main(String[] args) { - WeatherData weatherData = new WeatherData(); - CurrentConditionsDisplay currentConditionsDisplay = new CurrentConditionsDisplay(weatherData); - StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData); - - weatherData.setMeasurements(0, 0, 0); - weatherData.setMeasurements(1, 1, 1); - } -} -``` -执行结果 -```html -CurrentConditionsDisplay.update:0.0 0.0 0.0 -StatisticsDisplay.update:0.0 0.0 0.0 -CurrentConditionsDisplay.update:1.0 1.0 1.0 -StatisticsDisplay.update:1.0 1.0 1.0 -``` - -# 四、装饰模式 - -## 问题描述 - -设计不同种类的饮料,饮料可以添加配料,比如可以添加牛奶,并且支持动态添加新配料。每增加一种配料,该饮料的价格就会增加,要求计算一种饮料的价格。 - -## 模式定义 - -动态地将责任附加到对象上。在扩展功能上,装饰者提供了比继承更有弹性的替代方案。 - -下图表示在 DarkRoast 饮料上新增新添加 Mocha 配料,之后又添加了 Whip 配料。DarkRoast 被 Mocha 包裹,Mocha 又被 Whip 包裹。它们都继承自相同父类,都有 cost() 方法,外层类的 cost() 方法调用了内层类的 cost() 方法。 - -

- -## 模式类图 - -装饰者(Decorator)和具体组件(ConcreteComponent)都继承自组件(Component),具体组件的方法实现不需要依赖于其它对象,而装饰者组合了一个组件,这样它可以装饰其它装饰者或者具体组件。所谓装饰,就是把这个装饰者套在被装饰上,从而动态扩展被装饰者的功能。装饰者的方法有一部分是自己的,这属于它的功能,然后调用被装饰者的方法实现,从而也保留了被装饰者的功能。可以看到,具体组件应当是装饰层次的最低层,因为只有具体组件的方法实现不需要依赖于其它对象。 - -

- -## 问题的解决方案类图 - -

- -## 设计原则 - -类应该对扩展开放,对修改关闭:也就是添加新功能时不需要修改代码。在本章问题中该原则体现在,饮料可以动态添加新的配料,而不需要去修改饮料的代码。观察则模式也符合这个原则。不可能把所有的类设计成都满足这一原则,应当把该原则应用于最有可能发生改变的地方。 - -## Java I/O 中的装饰者模式 - -

- -## 代码实现 - -```java -public interface Beverage { - public double cost(); -} -``` -```java -public class HouseBlend implements Beverage{ - @Override - public double cost() { - return 1; - } -} -``` -```java -public class DarkRoast implements Beverage{ - @Override - public double cost() { - return 1; - } -} -``` -```java -public abstract class CondimentDecorator implements Beverage{ - protected Beverage beverage; -} -``` -```java -public class Mocha extends CondimentDecorator { - - public Mocha(Beverage beverage) { - this.beverage = beverage; - } - - @Override - public double cost() { - return 1 + beverage.cost(); - } -} -``` -```java -public class Milk extends CondimentDecorator { - - public Milk(Beverage beverage) { - this.beverage = beverage; - } - - @Override - public double cost() { - return 1 + beverage.cost(); - } -} -``` -```java -public class StartbuzzCoffee { - public static void main(String[] args) { - Beverage beverage = new HouseBlend(); - beverage = new Mocha(beverage); - beverage = new Milk(beverage); - System.out.println(beverage.cost()); - } -} -``` - -输出 - -```html -3.0 -``` - -# 五、简单工厂 - -## 问题描述 - -Pizza 类有很多子类,要求根据不同的情况用不同的子类实例化一个 Pizza 对象。 - -## 模式定义 - -简单工厂不是设计模式,更像是一种编程习惯。它把实例化的操作单独放到一个类中,这个类就成为简单工厂类,让简单工厂类来决定应该用哪个子类来实例化。 - -这样做能把客户类和具体子类的实现解耦,客户类不再需要知道有哪些子类以及应当实例化哪个子类。因为客户类往往有多个,如果不使用简单工厂,所有的客户类都要知道所有子类的细节。而且一旦子类发生改变,例如增加子类,那么所有的客户类都要进行修改。 - -

- -## 问题的解决方案类图 - -

- -## 代码实现 - -```java -public interface Pizza { - public void make(); -} -``` - -```java -public class CheesePizza implements Pizza{ - @Override - public void make() { - System.out.println("CheesePizza"); - } -} -``` - -```java -public class GreekPizza implements Pizza{ - @Override - public void make() { - System.out.println("GreekPizza"); - } -} -``` - -```java -public class SimplePizzaFactory { - public Pizza createPizza(String type) { - if (type.equals("cheese")) { - return new CheesePizza(); - } else if (type.equals("greek")) { - return new GreekPizza(); - } else { - throw new UnsupportedOperationException(); - } - } -} -``` - -```java -public class PizzaStore { - public static void main(String[] args) { - SimplePizzaFactory simplePizzaFactory = new SimplePizzaFactory(); - Pizza pizza = simplePizzaFactory.createPizza("cheese"); - pizza.make(); - } -} -``` - -运行结果 - -```java -CheesePizza -``` - -# 六、工厂方法模式 - -## 问题描述 - -每个地区的 PizzaStore 卖的 Pizza 虽然种类相同,但是都有自己的风味。一个客户点了纽约的 cheese 种类的 Pizza 和在芝加哥点的相同种类的 Pizza 是不同的。要求设计出满足条件的 PizzaStore。 - -## 模式定义 - -定义了一个创建对象的接口,但由子类决定要实例化哪个类。工厂方法把实例化推迟到子类。 - -## 模式类图 - -在简单工厂中,创建对象的是另一个类,而在工厂方法中,是由子类来创建对象。 - -下图中,Creator 有一个 anOperation() 方法,这个方法需要用到一组产品对象,这组产品对象由 factoryMethod() 方法创建。该方法是抽象的,需要由子类去实现。 - -

- -## 问题的解决方案类图 - -PizzaStore 有 orderPizza() 方法,顾客可以用它来下单。下单之后需要先使用 createPizza() 来制作 Pizza,这里的 createPizza() 就是 factoryMethod(),不同的 PizzaStore 子类实现了不同的 createPizza()。 - -

- -## 设计原则 - -依赖倒置原则:要依赖抽象,不要依赖具体类。听起来像是针对接口编程,不针对实现编程,但是这个原则说明了:不能让高层组件依赖底层组件,而且,不管高层或底层组件,两者都应该依赖于抽象。例如,下图中 Pizza 是抽象类,PizzaStore 和 Pizza 子类都依赖于 Pizza 这个抽象类。 - -

- -## 代码实现 - -```java -public interface Pizza { - public void make(); -} -``` -```java -public interface PizzaStore { - public Pizza orderPizza(String item); -} -``` -```java -public class NYStyleCheesePizza implements Pizza{ - @Override - public void make() { - System.out.println("NYStyleCheesePizza is making.."); - } -} -``` -```java -public class NYStyleVeggiePizza implements Pizza { - @Override - public void make() { - System.out.println("NYStyleVeggiePizza is making.."); - } -} -``` -```java -public class ChicagoStyleCheesePizza implements Pizza{ - @Override - public void make() { - System.out.println("ChicagoStyleCheesePizza is making.."); - } -} -``` -```java -public class ChicagoStyleVeggiePizza implements Pizza{ - @Override - public void make() { - System.out.println("ChicagoStyleVeggiePizza is making.."); - } -} -``` -```java -public class NYPizzaStore implements PizzaStore { - @Override - public Pizza orderPizza(String item) { - Pizza pizza = null; - if (item.equals("cheese")) { - pizza = new NYStyleCheesePizza(); - } else if (item.equals("veggie")) { - pizza = new NYStyleVeggiePizza(); - } else { - throw new UnsupportedOperationException(); - } - pizza.make(); - return pizza; - } -} -``` -```java -public class ChicagoPizzaStore implements PizzaStore { - @Override - public Pizza orderPizza(String item) { - Pizza pizza = null; - if (item.equals("cheese")) { - pizza = new ChicagoStyleCheesePizza(); - } else if (item.equals("veggie")) { - pizza = new ChicagoStyleVeggiePizza(); - } else { - throw new UnsupportedOperationException(); - } - pizza.make(); - return pizza; - } -} -``` -```java -public class PizzaTestDrive { - public static void main(String[] args) { - PizzaStore nyStore = new NYPizzaStore(); - nyStore.orderPizza("cheese"); - PizzaStore chicagoStore = new ChicagoPizzaStore(); - chicagoStore.orderPizza("cheese"); - } -} -``` - -运行结果 - -```html -NYStyleCheesePizza is making.. -ChicagoStyleCheesePizza is making.. -``` - -# 七、抽象工厂模式 - -## 模式定义 - -提供一个接口,用于创建 **相关的对象家族** 。 - -## 模式类图 - -抽象工厂模式创建的是对象家族,也就是很多对象而不是一个对象,并且这些对象是相关的,也就是说必须一起创建出来。而工厂模式只是用于创建一个对象,这和抽象工厂模式有很大不同。 - -抽象工厂模式用到了工厂模式来创建单一对象,在类图左部,AbstractFactory 中的 CreateProductA 和 CreateProductB 方法都是让子类来实现,这两个方法单独来看就是在创建一个对象,这符合工厂模式的定义。 - -至于创建对象的家族这一概念是在 Client 体现,Client 要通过 AbstractFactory 同时调用两个方法来创建出两个对象,在这里这两个对象就有很大的相关性,Client 需要同时创建出这两个对象。 - -从高层次来看,抽象工厂使用了组合,即 Cilent 组合了 AbstractFactory,而工厂模式使用了继承。 - -

- -## 解决方案类图 - -

- -## 代码实现 - -```java -public interface Dough { - public String doughType(); -} -``` -```java -public class ThickCrustDough implements Dough{ - - @Override - public String doughType() { - return "ThickCrustDough"; - } -} -``` -```java -public class ThinCrustDough implements Dough { - @Override - public String doughType() { - return "ThinCrustDough"; - } -} -``` -```java -public interface Sauce { - public String sauceType(); -} -``` -```java -public class MarinaraSauce implements Sauce { - @Override - public String sauceType() { - return "MarinaraSauce"; - } -} -``` -```java -public class PlumTomatoSauce implements Sauce { - @Override - public String sauceType() { - return "PlumTomatoSauce"; - } -} -``` -```java -public interface PizzaIngredientFactory { - public Dough createDough(); - public Sauce createSauce(); -} -``` -```java -public class NYPizzaIngredientFactory implements PizzaIngredientFactory{ - @Override - public Dough createDough() { - return new ThickCrustDough(); - } - - @Override - public Sauce createSauce() { - return new MarinaraSauce(); - } -} -``` -```java -public class ChicagoPizzaIngredientFactory implements PizzaIngredientFactory{ - @Override - public Dough createDough() { - return new ThinCrustDough(); - } - - @Override - public Sauce createSauce() { - return new PlumTomatoSauce(); - } -} -``` -```java -public class NYPizzaStore { - private PizzaIngredientFactory ingredientFactory; - - public NYPizzaStore() { - ingredientFactory = new NYPizzaIngredientFactory(); - } - - public void makePizza() { - Dough dough = ingredientFactory.createDough(); - Sauce sauce = ingredientFactory.createSauce(); - System.out.println(dough.doughType()); - System.out.println(sauce.sauceType()); - } -} -``` -```java -public class NYPizzaStoreTestDrive { - public static void main(String[] args) { - NYPizzaStore nyPizzaStore = new NYPizzaStore(); - nyPizzaStore.makePizza(); - } -} -``` - -运行结果 - -```html -ThickCrustDough -MarinaraSauce -``` - -# 八、单例模式 - -## 模式定义 +## 意图 确保一个类只有一个实例,并提供了一个全局访问点。 -## 模式类图 +## 类图 -使用一个私有构造器、一个私有静态变量以及一个公有静态函数来实现。 +使用一个私有构造函数、一个私有静态变量以及一个公有静态函数来实现。 私有构造函数保证了不能通过构造函数来创建对象实例,只能通过公有静态函数返回唯一的私有静态变量。 -

+

-## 懒汉式-线程不安全 +## 使用场景 + +- Logger Classes +- Configuration Classes +- Accesing resources in shared mode +- Factories implemented as Singletons + +## JDK 的使用 + +- [java.lang.Runtime#getRuntime()](http://docs.oracle.com/javase/8/docs/api/java/lang/Runtime.html#getRuntime%28%29) +- [java.awt.Desktop#getDesktop()](http://docs.oracle.com/javase/8/docs/api/java/awt/Desktop.html#getDesktop--) +- [java.lang.System#getSecurityManager()](http://docs.oracle.com/javase/8/docs/api/java/lang/System.html#getSecurityManager--) + +## 实现 + +### 懒汉式-线程不安全 以下实现中,私有静态变量 uniqueInstance 被延迟化实例化,这样做的好处是,如果没有用到该类,那么就不会实例化 uniqueInstance,从而节约资源。 -这个实现在多线程环境下是不安全的,如果多个线程能够同时进入`if(uniqueInstance == null)` ,那么就会多次实例化 uniqueInstance。 +这个实现在多线程环境下是不安全的,如果多个线程能够同时进入 if(uniqueInstance == null) ,那么就会多次实例化 uniqueInstance。 ```java public class Singleton { @@ -779,9 +71,9 @@ public class Singleton { } ``` -## 懒汉式-线程安全 +### 懒汉式-线程安全 -只需要对 `getUniqueInstance()` 方法加锁,那么在一个时间点只能有一个线程能够进入该方法,从而避免了对 uniqueInstance 进行多次实例化的问题。 +只需要对 getUniqueInstance() 方法加锁,那么在一个时间点只能有一个线程能够进入该方法,从而避免了对 uniqueInstance 进行多次实例化的问题。 但是这样有一个问题,就是当一个线程进入该方法之后,其它线程试图进入该方法都必须等待,因此性能上有一定的损耗。 @@ -794,7 +86,7 @@ public static synchronized Singleton getUniqueInstance() { } ``` -## 饿汉式-线程安全 +### 饿汉式-线程安全 线程不安全问题主要是由于 uniqueInstance 被实例化了多次,如果 uniqueInstance 采用直接实例化的话,就不会被实例化多次,也就不会产生线程不安全问题。但是直接实例化的方式也丢失了延迟实例化带来的节约资源的优势。 @@ -802,7 +94,7 @@ public static synchronized Singleton getUniqueInstance() { private static Singleton uniqueInstance = new Singleton(); ``` -## 双重校验锁-线程安全 +### 双重校验锁-线程安全 uniqueInstance 只需要被实例化一次,之后就可以直接使用了。加锁操作只需要对实例化那部分的代码进行。也就是说,只有当 uniqueInstance 没有被实例化时,才需要进行加锁。 @@ -829,7 +121,7 @@ public class Singleton { } ``` -考虑下面的实现,也就是只使用了一个 if 语句。在 uniqueInstance == null 的情况下,如果两个线程同时执行 if 语句,那么两个线程就会同时进入 if 语句块内。虽然在 if 语句块内有加锁操作,但是两个线程都会执行`uniqueInstance = new Singleton();`这条语句,只是早晚的问题,也就是说会进行两次实例化,从而产生了两个实例。因此必须使用双重校验锁,也就是需要使用两个 if 判断。 +考虑下面的实现,也就是只使用了一个 if 语句。在 uniqueInstance == null 的情况下,如果两个线程同时执行 if 语句,那么两个线程就会同时进入 if 语句块内。虽然在 if 语句块内有加锁操作,但是两个线程都会执行 uniqueInstance = new Singleton(); 这条语句,只是早晚的问题,也就是说会进行两次实例化,从而产生了两个实例。因此必须使用双重校验锁,也就是需要使用两个 if 判断。 ```java if (uniqueInstance == null) { @@ -839,962 +131,228 @@ if (uniqueInstance == null) { } ``` -# 九、命令模式 +# 四、简单工厂 -## 问题描述 +## 意图 -设计一个遥控器,它有很多按钮,每个按钮可以发起一个命令,命令会让一个家电完成相应操作。有非常多的家电,并且之后会增加家电。 +在创建一个对象时不向客户暴露内部细节; -

+## 类图 -有非常多的家电,并且之后会增加家电。 +简单工厂不是设计模式,更像是一种编程习惯。它把实例化的操作单独放到一个类中,这个类就成为简单工厂类,让简单工厂类来决定应该用哪个子类来实例化。 -

+

-## 模式定义 +这样做能把客户类和具体子类的实现解耦,客户类不再需要知道有哪些子类以及应当实例化哪个子类。因为客户类往往有多个,如果不使用简单工厂,所有的客户类都要知道所有子类的细节。而且一旦子类发生改变,例如增加子类,那么所有的客户类都要进行修改。 -将命令封装成对象,以便使用不同的命令来参数化其它对象。 - -## 问题的解决方案类图 - -- RemoteControl 是遥控器,它可以为每个按钮设置命令对象,并且执行命令。 - -- Command 是命令对象。 - -- Light(电灯)是命令真正的执行者。 - -- RemoteLoader 是客户端,应该注意它与 RemoteControl 的区别。因为 RemoteControl 不能主动地调用自身的方法,因此也就不能当成是客户端。客户端好比人,只有人才能去真正去使用遥控器。 - -

- -## 模式类图 - -

- -## 代码实现 - -```java -public interface Command { - public void execute(); -} -``` - -```java -public class Light { - - public void on() { - System.out.println("Light is on!"); - } - - public void off() { - System.out.println("Light is off!"); - } -} -``` - -```java -public class LightOnCommand implements Command{ - Light light; - - public LightOnCommand(Light light) { - this.light = light; - } - - @Override - public void execute() { - light.on(); - } -} -``` - -```java -/** - * 遥控器类 - */ -public class SimpleRemoteControl { - Command slot; - - public SimpleRemoteControl() { - - } - - public void setCommand(Command command) { - this.slot = command; - } - - public void buttonWasPressed() { - slot.execute(); - } - -} -``` - -```java -/** - * 客户端 - */ -public class RemoteLoader { - public static void main(String[] args) { - SimpleRemoteControl remote = new SimpleRemoteControl(); - Light light = new Light(); - LightOnCommand lightOnCommand = new LightOnCommand(light); - remote.setCommand(lightOnCommand); - remote.buttonWasPressed(); - } -} -``` - -输出 - -```html -Light is on! -``` - -# 十、适配器模式 - -## 模式定义 - -将一个类的接口,转换为客户期望的另一个接口。适配器让原本不兼容的类可以合作无间。 - -

- -## 模式类图 - -适配器(Adapter)组合一个适配者(Adaptee),Adapter 把操作委托给 Adaptee。 - -

- -## 问题描述 - -鸭子(Duck)和火鸡(Turkey)拥有不同的叫声,Duck 的叫声调用 quack() 方法,而 Turkey 调用 gobble() 方法。 - -要求将 Turkey 的 gobble() 方法适配成 Duck 的 quack() 方法,从而让火鸡冒充鸭子! - -## 问题的解决方案类图 - -

- -## 代码实现 - -```java -public interface Duck { - public void quack(); -} -``` - -```java -public interface Turkey { - public void gobble(); -} -``` - -```java -public class WildTurkey implements Turkey{ - @Override - public void gobble() { - System.out.println("gobble!"); - } -} -``` - -```java -public class TurkeyAdapter implements Duck{ - Turkey turkey; - - public TurkeyAdapter(Turkey turkey) { - this.turkey = turkey; - } - - @Override - public void quack() { - turkey.gobble(); - } -} -``` - -```java -public class DuckTestDrive { - public static void main(String[] args) { - Turkey turkey = new WildTurkey(); - Duck duck = new TurkeyAdapter(turkey); - duck.quack(); - } -} -``` - -运行结果 - -```html -gobble! -``` - -# 十、外观模式 - -## 模式定义 - -提供了一个统一的接口,用来访问子系统中的一群接口,从而让子系统更容易使用。 - -## 模式类图 - -

- -## 问题描述 - -家庭影院中有众多电器,当要进行观看电影时需要对很多电器进行操作。要求简化这些操作,使得家庭影院类只提供一个简化的接口,例如提供一个看电影相关的接口。 - -

- -## 解决方案类图 - -

- -## 设计原则 - -**最少知识原则** :只和你的密友谈话。也就是客户对象所需要交互的对象应当尽可能少。 - -## 代码实现 - -过于简单,无实现。 - -# 十一、模板方法模式 - -## 模式定义 - -在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。 - -模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。 - -## 模式类图 - -模板方法 templateMethod() 定义了算法的骨架,确定了 primitiveOperation1() 和 primitiveOperation2() 方法执行的顺序,而 primitiveOperation1() 和 primitiveOperation2() 让子类去实现。 - -

- -## 问题描述 - -冲咖啡和冲茶都有类似的流程,但是某些步骤会有点不一样,要求复用那些相同步骤的代码。 - -

- -## 问题的解决方案类图 - -prepareRecipe() 方法就是模板方法,它确定了其它四个方法的具体执行步骤。其中 brew() 和 addCondiments() 方法在子类中实现。 - -

- -## 设计原则 - -**好莱坞原则** :别调用(打电话给)我们,我们会调用(打电话给)你。这一原则可以防止依赖腐败,即防止高层组件依赖低层组件,低层组件又依赖高层组件。该原则在模板方法的体现为,只有父类会调用子类,子类不会调用父类。 - -## 钩子 - -某些步骤在不同实现中可有可无,可以先定义一个什么都不做的方法,把它加到模板方法中,如果子类需要它就覆盖默认实现并加上自己的实现。 - -## 代码实现 - -```java -public abstract class CaffeineBeverage { - - final void prepareRecipe(){ - boilWater(); - brew(); - pourInCup(); - addCondiments(); - } - - abstract void brew(); - - abstract void addCondiments(); - - void boilWater(){ - System.out.println("boilWater"); - } - - void pourInCup(){ - System.out.println("pourInCup"); - } -} -``` - -```java -public class Coffee extends CaffeineBeverage{ - @Override - void brew() { - System.out.println("Coffee.brew"); - } - - @Override - void addCondiments() { - System.out.println("Coffee.addCondiments"); - } -} -``` - -```java -public class Tea extends CaffeineBeverage{ - @Override - void brew() { - System.out.println("Tea.brew"); - } - - @Override - void addCondiments() { - System.out.println("Tea.addCondiments"); - } -} -``` - -```java -public class CaffeineBeverageTestDrive { - public static void main(String[] args) { - CaffeineBeverage caffeineBeverage = new Coffee(); - caffeineBeverage.prepareRecipe(); - System.out.println("-----------"); - caffeineBeverage = new Tea(); - caffeineBeverage.prepareRecipe(); - } -} -``` - -运行结果 - -```html -boilWater -Coffee.brew -pourInCup -Coffee.addCondiments ------------ -boilWater -Tea.brew -pourInCup -Tea.addCondiments -``` - -# 十二、迭代器模式 - -## 模式定义 - -提供顺序访问一个聚合对象中的各个元素的方法,而又不暴露聚合对象内部的表示。 - -## 模式类图 - -- Aggregate 是聚合类,其中 createIterator() 方法可以产生一个 Iterator; - -- Iterator 主要定义了 hasNext() 和 next() 方法。 - -- Client 组合了 Aggregate,为了迭代遍历 Aggregate,也需要组合 Iterator。 - -

- -## 代码实现 - -```java -public class Aggregate { - - private int[] items; - - public Aggregate() { - items = new int[10]; - for (int i = 0; i < items.length; i++) { - items[i] = i; - } - } - - public Iterator createIterator() { - return new ConcreteIterator(items); - } - -} -``` - -```java -public interface Iterator { - boolean hasNext(); - int next(); -} -``` - -```java -public class ConcreteIterator implements Iterator { - - private int[] items; - private int position = 0; - - public ConcreteIterator(int[] items) { - this.items = items; - } - - @Override - public boolean hasNext() { - return position < items.length; - } - - @Override - public int next() { - return items[position++]; - } -} -``` -```java -public class Client { - public static void main(String[] args) { - Aggregate aggregate = new Aggregate(); - Iterator iterator = aggregate.createIterator(); - while(iterator.hasNext()){ - System.out.println(iterator.next()); - } - } -} -``` -运行结果 -```html -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -``` - -## Java 内置的迭代器 - -需要让聚合类实现 Iterable 接口,该接口有一个 iterator() 方法会返回一个 Iterator 对象。 - -可以使用 foreach 循环来顺序访问聚合对象中的每个元素。 - -Java 中的集合类基本都实现了 Iterable 接口。 - -```java -import java.util.Iterator; - -public class Aggregate implements Iterable{ - - private int[] items; - - public Aggregate() { - items = new int[10]; - for (int i = 0; i < items.length; i++) { - items[i] = i; - } - } - - @Override - public Iterator iterator() { - return new ConcreteIterator(items); - } -} -``` -```java -import java.util.Iterator; - -public class ConcreteIterator implements Iterator { - - private int[] items; - private int position = 0; - - public ConcreteIterator(int[] items) { - this.items = items; - } - - @Override - public boolean hasNext() { - return position < items.length; - } - - @Override - public Integer next() { - return items[position++]; - } -} -``` -```java -public class Client { - public static void main(String[] args) { - Aggregate aggregate = new Aggregate(); - for (int item : aggregate) { - System.out.println(item); - } - } -} -``` - -# 十三、组合模式 - -## 设计原则 - -一个类应该只有一个引起它改变的原因。 - -## 模式定义 - -允许将对象组合成树形结构来表现“整体/部分”关系。 - -组合能让客户以一致的方式处理个别对象以及组合对象。 - -## 模式类图 - -组件(Component)类是组合类(Composite)和叶子类(Leaf)的父类,可以把组合类看成是树的中间节点。 - -组合对象拥有一个组件对象,因此组合对象的操作可以委托给组件对象去处理,而组件对象可以是另一个组合对象或者叶子对象。 - -

- -## 代码实现 - -```java -public abstract class Component { - protected String name; - - public Component(String name) { - this.name = name; - } - - abstract public void addChild(Component component); - - public void print() { - print(0); - } - - abstract protected void print(int level); -} -``` - -```java -public class Leaf extends Component { - public Leaf(String name) { - super(name); - } - - @Override - public void addChild(Component component) { - throw new UnsupportedOperationException(); // 牺牲透明性换取单一职责原则,这样就不用考虑是叶子节点还是组合节点 - } - - @Override - protected void print(int level) { - for (int i = 0; i < level; i++) { - System.out.print("--"); - } - System.out.println("left:" + name); - } -} -``` - -```java -public class Composite extends Component { - - private List childs; - - public Composite(String name) { - super(name); - childs = new ArrayList<>(); - } - - @Override - public void addChild(Component component) { - childs.add(component); - } - - @Override - protected void print(int level) { - for (int i = 0; i < level; i++) { - System.out.print("--"); - } - System.out.println("Composite:" + name); - for (Component component : childs) { - component.print(level + 1); - } - } -} -``` +如果存在下面这种代码,就需要使用简单工厂将对象实例化的部分放到简单工厂中。 ```java public class Client { public static void main(String[] args) { - Composite root = new Composite("root"); - Component node1 = new Leaf("1"); - Component node2 = new Composite("2"); - Component node3 = new Leaf("3"); - root.addChild(node1); - root.addChild(node2); - root.addChild(node3); - Component node21 = new Leaf("21"); - Component node22 = new Composite("22"); - node2.addChild(node21); - node2.addChild(node22); - Component node221 = new Leaf("221"); - node22.addChild(node221); - root.print(); - } -} -``` -运行结果 - -```html -Composite:root ---left:1 ---Composite:2 -----left:21 -----Composite:22 -------left:221 ---left:3 -``` - -# 十四、状态模式 - -## 模式定义 - -允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它所属的类。 - -## 模式类图 - -Context 的 request() 方法委托给 State 对象去处理。当 Context 组合的 State 对象发生改变时,它的行为也就发生了改变。 - -

- -## 与策略模式的比较 - -状态模式的类图和策略模式一样,并且都是能够动态改变对象的行为。 - -但是状态模式是通过状态转移来改变 Context 所组合的 State 对象,而策略模式是通过 Context 本身的决策来改变组合的 Strategy 对象。 - -所谓的状态转移,是指 Context 在运行过程中由于一些条件发生改变而使得 State 对象发生改变,注意必须要是在运行过程中。 - -状态模式主要是用来解决状态转移的问题,当状态发生转移了,那么 Context 对象就会改变它的行为;而策略模式主要是用来封装一组可以互相替代的算法族,并且可以根据需要动态地去替换 Context 需要使用哪个算法。 - -## 问题描述 - -糖果销售机有多种状态,每种状态下销售机有不同的行为,状态可以发生转移,使得销售机的行为也发生改变。 - -

- -## 直接解决方案 - -在糖果机的每个操作函数里面,判断当前的状态,根据不同的状态进行不同的处理,并且发生不同的状态转移。 - -这种解决方案在需要增加状态的时候,必须对每个操作的代码都进行修改。 - -

- -## 代码实现 - -糖果销售机即 Context。 - -下面的实现中每个 State 都组合了 Context 对象,这是因为状态转移的操作在 State 对象中,而状态转移过程又必须改变 Context 对象的 state 对象,因此 State 必须组合 Context 对象。 - -```java -public interface State { - /** - * 投入 25 分钱 - */ - void insertQuarter(); - - /** - * 退回 25 分钱 - */ - void ejectQuarter(); - - /** - * 转动曲柄 - */ - void turnCrank(); - - /** - * 发放糖果 - */ - void dispense(); -} -``` -```java -public class HasQuarterState implements State{ - - private GumballMachine gumballMachine; - - public HasQuarterState(GumballMachine gumballMachine){ - this.gumballMachine = gumballMachine; - } - - @Override - public void insertQuarter() { - System.out.println("You can't insert another quarter"); - } - - @Override - public void ejectQuarter() { - System.out.println("Quarter returned"); - gumballMachine.setState(gumballMachine.getNoQuarterState()); - } - - @Override - public void turnCrank() { - System.out.println("You turned..."); - gumballMachine.setState(gumballMachine.getSoldState()); - } - - @Override - public void dispense() { - System.out.println("No gumball dispensed"); - } -} -``` -```java -public class NoQuarterState implements State { - - GumballMachine gumballMachine; - - public NoQuarterState(GumballMachine gumballMachine) { - this.gumballMachine = gumballMachine; - } - - @Override - public void insertQuarter() { - System.out.println("You insert a quarter"); - gumballMachine.setState(gumballMachine.getHasQuarterState()); - } - - @Override - public void ejectQuarter() { - System.out.println("You haven't insert a quarter"); - } - - @Override - public void turnCrank() { - System.out.println("You turned, but there's no quarter"); - } - - @Override - public void dispense() { - System.out.println("You need to pay first"); - } -} -``` -```java -public class SoldOutState implements State { - - GumballMachine gumballMachine; - - public SoldOutState(GumballMachine gumballMachine) { - this.gumballMachine = gumballMachine; - } - - @Override - public void insertQuarter() { - System.out.println("You can't insert a quarter, the machine is sold out"); - } - - @Override - public void ejectQuarter() { - System.out.println("You can't eject, you haven't inserted a quarter yet"); - } - - @Override - public void turnCrank() { - System.out.println("You turned, but there are no gumballs"); - } - - @Override - public void dispense() { - System.out.println("No gumball dispensed"); - } -} -``` -```java -public class SoldState implements State { - - GumballMachine gumballMachine; - - public SoldState(GumballMachine gumballMachine) { - this.gumballMachine = gumballMachine; - } - - @Override - public void insertQuarter() { - System.out.println("Please wait, we're already giving you a gumball"); - } - - @Override - public void ejectQuarter() { - System.out.println("Sorry, you already turned the crank"); - } - - @Override - public void turnCrank() { - System.out.println("Turning twice doesn't get you another gumball!"); - } - - @Override - public void dispense() { - gumballMachine.releaseBall(); - if(gumballMachine.getCount()>0){ - gumballMachine.setState(gumballMachine.getNoQuarterState()); - } else{ - System.out.println("Oops, out of gumballs"); - gumballMachine.setState(gumballMachine.getSoldOutState()); - } - } -} -``` -```java -public class GumballMachine { - - private State soldOutState; - private State noQuarterState; - private State hasQuarterState; - private State soldState; - - private State state; - private int count = 0; - - public GumballMachine(int numberGumballs) { - count = numberGumballs; - soldOutState = new SoldOutState(this); - noQuarterState = new NoQuarterState(this); - hasQuarterState = new HasQuarterState(this); - soldState = new SoldState(this); - - if (numberGumballs > 0) { - state = noQuarterState; + int type = 1; + Product product; + if (type == 1) { + product = new ConcreteProduct1(); + } else if (type == 2) { + product = new ConcreteProduct2(); } else { - state = soldOutState; + product = new ConcreteProduct(); } } - - public void insertQuarter() { - state.insertQuarter(); - } - - public void ejectQuarter() { - state.ejectQuarter(); - } - - public void turnCrank() { - state.turnCrank(); - state.dispense(); - } - - public void setState(State state) { - this.state = state; - } - - public void releaseBall() { - System.out.println("A gumball comes rolling out the slot..."); - if (count != 0) { - count -= 1; - } - } - - public State getSoldOutState() { - return soldOutState; - } - - public State getNoQuarterState() { - return noQuarterState; - } - - public State getHasQuarterState() { - return hasQuarterState; - } - - public State getSoldState() { - return soldState; - } - - public int getCount() { - return count; - } } ``` + +## 实现 + ```java -public class GumballMachineTestDrive { +public interface Product { +} +``` - public static void main(String[] args) { - GumballMachine gumballMachine = new GumballMachine(5); +```java +public class ConcreteProduct implements Product{ +} +``` - gumballMachine.insertQuarter(); - gumballMachine.turnCrank(); +```java +public class ConcreteProduct1 implements Product{ +} +``` - gumballMachine.insertQuarter(); - gumballMachine.ejectQuarter(); - gumballMachine.turnCrank(); +```java +public class ConcreteProduct2 implements Product{ +} +``` - gumballMachine.insertQuarter(); - gumballMachine.turnCrank(); - gumballMachine.insertQuarter(); - gumballMachine.turnCrank(); - gumballMachine.ejectQuarter(); - - gumballMachine.insertQuarter(); - gumballMachine.insertQuarter(); - gumballMachine.turnCrank(); - gumballMachine.insertQuarter(); - gumballMachine.turnCrank(); - gumballMachine.insertQuarter(); - gumballMachine.turnCrank(); +```java +public class SimpleFactory { + public Product createProduct(int type) { + if (type == 1) { + return new ConcreteProduct1(); + } else if (type == 2) { + return new ConcreteProduct2(); + } + return new ConcreteProduct(); } } ``` -运行结果 -```html -You insert a quarter -You turned... -A gumball comes rolling out the slot... -You insert a quarter -Quarter returned -You turned, but there's no quarter -You need to pay first -You insert a quarter -You turned... -A gumball comes rolling out the slot... -You insert a quarter -You turned... -A gumball comes rolling out the slot... -You haven't insert a quarter -You insert a quarter -You can't insert another quarter -You turned... -A gumball comes rolling out the slot... -You insert a quarter -You turned... -A gumball comes rolling out the slot... -Oops, out of gumballs -You can't insert a quarter, the machine is sold out -You turned, but there are no gumballs -No gumball dispensed + +```java +public class Client { + public static void main(String[] args) { + SimpleFactory simpleFactory = new SimpleFactory(); + Product product = simpleFactory.createProduct(1); + } +} ``` -# 十五、代理模式 -# 十六、MVC +# 五、工厂方法模式 -## 传统 MVC +## 意图 -视图使用组合模式,模型使用了观察者模式,控制器使用了策略模式。 +定义了一个创建对象的接口,但由子类决定要实例化哪个类。工厂方法把实例化推迟到子类。 -

+## 类图 -## Web 中的 MVC +在简单工厂中,创建对象的是另一个类,而在工厂方法中,是由子类来创建对象。 -模式不再使用观察者模式。 +下图中,Factory 有一个 doSomethind() 方法,这个方法需要用到一组产品对象,这组产品对象由 factoryMethod() 方法创建。该方法是抽象的,需要由子类去实现。 -

+

-# 十七、与设计模式相处 +## 实现 -## 定义 +```java +public abstract class Factory { + abstract public Product factoryMethod(); + public void doSomethind() { + Product product = factoryMethod(); + // do something with the product + } +} +``` -在某情境下,针对某问题的某种解决方案。 +```java +public class ConcreteFactory extends Factory { + public Product factoryMethod() { + return new ConcreteProduct(); + } +} +``` -## 何时使用 +```java +public class ConcreteFactory1 extends Factory{ + public Product factoryMethod() { + return new ConcreteProduct1(); + } +} +``` -过度使用设计模式可能导致代码被过度工程化,应该总是用最简单的解决方案完成工作,并在真正需要模式的地方才使用它。 +```java +public class ConcreteFactory2 extends Factory { + public Product factoryMethod() { + return new ConcreteProduct2(); + } +} +``` -## 反模式 +# 六、抽象工厂模式 -不好的解决方案来解决一个问题。主要作用是为了警告人们不要使用这些解决方案。 +## 意图 -## 模式分类 +提供一个接口,用于创建 **相关的对象家族** 。 -

+## 类图 + +

+ +抽象工厂模式创建的是对象家族,也就是很多对象而不是一个对象,并且这些对象是相关的,也就是说必须一起创建出来。而工厂模式只是用于创建一个对象,这和抽象工厂模式有很大不同。 + +抽象工厂模式用到了工厂模式来创建单一对象,在类图左部,AbstractFactory 中的 createProductA 和 createProductB 方法都是让子类来实现,这两个方法单独来看就是在创建一个对象,这符合工厂模式的定义。 + +至于创建对象的家族这一概念是在 Client 体现,Client 要通过 AbstractFactory 同时调用两个方法来创建出两个对象,在这里这两个对象就有很大的相关性,Client 需要同时创建出这两个对象。 + +从高层次来看,抽象工厂使用了组合,即 Cilent 组合了 AbstractFactory,而工厂模式使用了继承。 + + +## 代码实现 + +```java +public class AbstractProductA { +} +``` + +```java +public class AbstractProductB { +} +``` + +```java +public class ProductA1 extends AbstractProductA { +} +``` + +```java +public class ProductA2 extends AbstractProductA { +} +``` + +```java +public class ProductB1 extends AbstractProductB{ +} +``` + +```java +public class ProductB2 extends AbstractProductB{ +} +``` + +```java +public abstract class AbstractFactory { + abstract AbstractProductA createProductA(); + abstract AbstractProductB createProductB(); +} +``` + +```java +public class ConcreteFactory1 extends AbstractFactory{ + AbstractProductA createProductA() { + return new ProductA1(); + } + + AbstractProductB createProductB() { + return new ProductB1(); + } +} +``` + +```java +public class ConcreteFactory2 extends AbstractFactory { + AbstractProductA createProductA() { + return new ProductA2(); + } + + AbstractProductB createProductB() { + return new ProductB2(); + } +} +``` + +```java +public class Client { + public static void main(String[] args) { + AbstractFactory abstractFactory = new ConcreteFactory1(); + AbstractProductA productA = abstractFactory.createProductA(); + abstractFactory = new ConcreteFactory2(); + productA = abstractFactory.createProductA(); + } +} +``` + +# 参考资料 + +- 弗里曼. Head First 设计模式 [M]. 中国电力出版社, 2007. +- [Design Patterns](http://www.oodesign.com/) +- [Design patterns implemented in Java](http://java-design-patterns.com/) diff --git a/notes/重构.md b/notes/重构.md index bc05e60a..e74d3cc5 100644 --- a/notes/重构.md +++ b/notes/重构.md @@ -107,6 +107,7 @@ * [10. 塑造模板函数](#10-塑造模板函数) * [11. 以委托取代继承](#11-以委托取代继承) * [12. 以继承取代委托](#12-以继承取代委托) +* [参考资料](#参考资料) @@ -1250,3 +1251,7 @@ public Manager(String name, String id, int grade) { 你在两个类之间使用委托关系,并经常为整个接口编写许多极简单的委托函数。 让委托类继承受托类。 + +# 参考资料 + +- MartinFowler, 福勒, 贝克, 等. 重构: 改善既有代码的设计 [M]. 电子工业出版社, 2011. diff --git a/notes/面向对象思想.md b/notes/面向对象思想.md index b4e5f032..02c7222d 100644 --- a/notes/面向对象思想.md +++ b/notes/面向对象思想.md @@ -6,21 +6,25 @@ * [封装](#封装) * [继承](#继承) * [多态](#多态) -* [三、UML](#三uml) - * [类图](#类图) - * [时序图](#时序图) +* [三、类图](#三类图) + * [泛化关系 (Generalization)](#泛化关系-generalization) + * [实现关系 (Realization)](#实现关系-realization) + * [聚合关系 (Aggregation)](#聚合关系-aggregation) + * [组合关系 (Composition)](#组合关系-composition) + * [关联关系 (Association)](#关联关系-association) + * [依赖关系 (Dependency)](#依赖关系-dependency) * [参考资料](#参考资料) # 一、设计原则 -设计原则可以帮助我们避免那些糟糕的设计。 - ## S.O.L.I.D + + | 简写 | 全拼 | 中文翻译 | -| -- | -- | -- | +| :--: | :--: | :--: | | SRP | The Single Responsibility Principle | 单一责任原则 | | OCP | The Open Closed Principle | 开放封闭原则 | | LSP | The Liskov Substitution Principle | 里氏替换原则 | @@ -55,14 +59,13 @@ > 不应该强迫客户依赖于它们不用的方法。 -因此使用多个专门的接口比使用单一的总接口总要好。 +因此使用多个专门的接口比使用单一的总接口要好。 ### 5. 依赖倒置原则 -> 高层模块不应该依赖于低层模块,二者都应该依赖于抽象。 -> 抽象不应该依赖于细节,细节应该依赖于抽象。 +> 高层模块不应该依赖于低层模块,二者都应该依赖于抽象;
抽象不应该依赖于细节,细节应该依赖于抽象。 -高层模块包含一个应用程序中重要的策略选择和业务模块,如果高层模块依赖于底层模块,那么底层模块的改动就会直接影响到高层模块,从而迫使高层模块也需要改动。 +高层模块包含一个应用程序中重要的策略选择和业务模块,如果高层模块依赖于低层模块,那么低层模块的改动就会直接影响到高层模块,从而迫使高层模块也需要改动。 依赖于抽象意味着: @@ -75,7 +78,7 @@ 除了上述的经典原则,在实际开发中还有下面这些常见的设计原则。 | 简写 | 全拼 | 中文翻译 | -| -- | -- | -- | +| :--: | :--: | :--: | |LOD| The Law of Demeter | 迪米特法则 | |CRP| The Composite Reuse Principle | 合成复用原则 | |CCP| The Common Closure Principle | 共同封闭原则 | @@ -84,7 +87,7 @@ ### 1. 迪米特法则 -迪米特法则又叫作最少知道原则(Least Knowledge Principle 简写LKP),就是说一个对象应当对其他对象有尽可能少的了解,不和陌生人说话。 +迪米特法则又叫作最少知识原则(Least Knowledge Principle,简写 LKP),就是说一个对象应当对其他对象有尽可能少的了解,不和陌生人说话。 ### 2. 合成复用原则 @@ -197,147 +200,47 @@ public class Music { } ``` -# 三、UML +# 三、类图 -## 类图 +## 泛化关系 (Generalization) -### 1. 继承相关 +用来描述继承关系,在 Java 中使用 extends 关键字。 -继承有两种形式 : 泛化(Generalize)和实现(Realize),表现为 IS-A 关系。 +

-#### 泛化关系 (Generalize) +## 实现关系 (Realization) -从具体类中继承。 +用来实现一个接口,在 Java 中使用 implement 关键字。 -

+

-#### 实现关系 (Realize) +## 聚合关系 (Aggregation) -从抽象类或者接口中继承。 +表示整体由部分组成,但是整体和部分不是强依赖的,整体不存在了部分还是会存在。 -

+

-### 2. 整体和部分 - -#### 聚合关系 (Aggregation) - -表示整体由部分组成,但是整体和部分不是强依赖的,整体不存在了部分还是会存在。以下表示 B 由 A 组成: - -

- -#### 组合关系 (Composition) +## 组合关系 (Composition) 和聚合不同,组合中整体和部分是强依赖的,整体不存在了部分也不存在了。比如公司和部门,公司没了部门就不存在了。但是公司和员工就属于聚合关系了,因为公司没了员工还在。 -

+

-### 3. 相互联系 - -#### 关联关系 (Association) +## 关联关系 (Association) 表示不同类对象之间有关联,这是一种静态关系,与运行过程的状态无关,在最开始就可以确定。因此也可以用 1 对 1、多对 1、多对多这种关联关系来表示。比如学生和学校就是一种关联关系,一个学校可以有很多学生,但是一个学生只属于一个学校,因此这是一种多对一的关系,在运行开始之前就可以确定。 -

+

-#### 依赖关系 (Dependency) +## 依赖关系 (Dependency) -和关联关系不同的是 , 依赖关系是在运行过程中起作用的。一般依赖作为类的构造器或者方法的参数传入。双向依赖时一种不好的设计。 +和关联关系不同的是,依赖关系是在运行过程中起作用的。A 类和 B 类是依赖关系主要有三种形式: -

+1. A 类是 B 类中的(某中方法的)局部变量; +2. A 类是 B 类方法当中的一个参数; +3. A 类向 B 类发送消息,从而影响 B 类发生变化; -## 时序图 - -### 1. 定义 - -时序图描述了对象之间传递消息的时间顺序,它用来表示用例的行为顺序。它的主要作用是通过对象间的交互来描述用例(注意是对象),从而寻找类的操作。 - -### 2. 赤壁之战时序图 - -从虚线从上往下表示时间的推进。 - -

- -可见,通过时序图可以知道每个类具有以下操作: - -```java -publc class 刘备 { - public void 应战 (); -} - -publc class 孔明 { - public void 拟定策略 (); - public void 联合孙权 (); - private void 借东风火攻 (); -} - -public class 关羽 { - public void 防守荊州 (); -} - -public class 张飞 { - public void 防守荆州前线 (); -} - -public class 孙权 { - public void 领兵相助 (); -} -``` - -### 3. 活动图、时序图之间的关系 - -活动图示从用户的角度来描述用例; - -时序图是从计算机的角度(对象间的交互)描述用例。 - -### 4. 类图与时序图的关系 - -类图描述系统的静态结构,时序图描述系统的动态行为。 - -### 5. 时序图的组成 - -#### 对象 - -有三种表现形式 - -

- -在画图时,应该遵循以下原则: - -1. 把交互频繁的对象尽可能地靠拢。 - -2. 把初始化整个交互活动的对象(有时是一个参与者)放置在最左边。 - -#### 生命线 - -生命线从对象的创建开始到对象销毁时终止 - -

- -#### 消息 - -对象之间的交互式通过发送消息来实现的。 - -消息有 4 种类型: - -1\. 简单消息,不区分同步异步。 - -

- -2\. 同步消息,发送消息之后需要暂停活动来等待回应。 - -

- -3\. 异步消息,发送消息之后不需要等待。 - -

- -4\. 返回消息,可选。 - -#### 激活 - -生命线上的方框表示激活状态,其它时间处于休眠状态。 - -

+

# 参考资料 diff --git a/other/alipay.md b/other/alipay.md deleted file mode 100644 index 278ba5ba..00000000 --- a/other/alipay.md +++ /dev/null @@ -1,3 +0,0 @@ -
- -
diff --git a/other/alipay.png b/other/alipay.png deleted file mode 100644 index 6402c854..00000000 Binary files a/other/alipay.png and /dev/null differ diff --git a/other/download.md b/other/download.md deleted file mode 100644 index 96d0863d..00000000 --- a/other/download.md +++ /dev/null @@ -1,45 +0,0 @@ - -# 网络 - -          - -# 操作系统 - -          - -# 算法 - -          - -# 设计模式 - -    - -# 数据库 - -       - -# Redis - -    - -# Java - -       - -# C++ - -             - -# 工具 - -    - -# 编码实践 - -                   - -# 科普 - -                - diff --git a/other/download2.md b/other/download2.md deleted file mode 100644 index 9d5c7622..00000000 --- a/other/download2.md +++ /dev/null @@ -1,76 +0,0 @@ -# 关于 - -计算机经典书籍 PDF 下载 - -# 网络 - -- [计算机网络.pdf](https://pan.baidu.com/s/1EXaJbNckzuQMOCyamzjL_Q) -- [TCP/IP详解.pdf](https://pan.baidu.com/s/1oBbA9LOevcJ_reg8y5kOvw) -- [图解 HTTP.pdf](https://pan.baidu.com/s/1M0AHXqG9sP9Bxne6u0JK8A) -- [图解 TCP/IP.pdf](https://pan.baidu.com/s/1y0P-VFlWKdOPW7YB60OWlw) - -# 操作系统 - -- [计算机操作系统.pdf](https://pan.baidu.com/s/1C-MgvslLKd1buwmebti6Qg) -- [鸟哥的 Linux 私房菜](https://pan.baidu.com/s/1Qm2G4rghPorQeH5J9fDHTg) -- [深入理解计算机系统.pdf](https://pan.baidu.com/s/1OoyVI90fK1Q9eixzH9jnpQ) -- [现代操作系统.pdf](https://pan.baidu.com/s/12mTkrpLsb7tz11cGn_KZ4w) - -# 算法 - -- [算法.pdf](https://pan.baidu.com/s/1Va1R66d13ynmita8nfkRPg) -- [剑指 Offer.pdf](https://pan.baidu.com/s/1HmGwXvTcHDrQnUAL1wWE3g) -- [编程之美.pdf](https://pan.baidu.com/s/1SZGUbvKpKOomM-iYxe_GGw) -- [程序员代码面试指南.pdf](https://pan.baidu.com/s/10EoXyW33MnYJUX5YeD5pPg) - -# 设计模式 - -- [Head First 设计模式.pdf](https://pan.baidu.com/s/1JOO4M3c6EGB5xHz_-aGtDQ) -- [设计模式 可复用面试对象软件的基础.pdf](https://pan.baidu.com/s/1n41aEgGuRg9hQ-9iwOxc5A) - -# 数据库 - -- [数据库系统概论.pdf](https://pan.baidu.com/s/1xhYsZUi2fugLf9jxSWA0pQ) -- [高性能 MySQL.pdf](https://pan.baidu.com/s/1aXRWznphuiEc4XRXpM1qLA) -- [MySQL 必知必会.pdf](https://pan.baidu.com/s/182JK19-rvbISYAv4aLk7xg) - -# Redis - -- [Redis 设计与实现.pdf](https://pan.baidu.com/s/1XovYaApdsVsd97pLCwAvpA) -- [Reids 实战.pdf](https://pan.baidu.com/s/1bfbiPjoBEaNUs6qLWVEIJw) - -# Java - -- [Java 编程思想.pdf](https://pan.baidu.com/s/1iNBkY9ANUcmeSp4VjBGhRQ) -- [深入理解 Java 虚拟机.pdf](https://pan.baidu.com/s/1zdATX8Qs-RMk6DN7iqECYw) -- [Java 并发编程实战.pdf](https://pan.baidu.com/s/1LkPVPrT_3BYFkfxieBkeVw) - -# C++ - -- [C++ Promer 第五版.pdf](https://pan.baidu.com/s/1VhhqN7oVcrv0KhF32CXRLQ) -- [C 和指针.pdf](https://pan.baidu.com/s/1u3-QrdnkHo5ScUK84v7C5w) -- [Unix 环境高级编程.pdf](https://pan.baidu.com/s/1K6xm3YlV53trCxyGR0j_gQ) -- [Unix 网络编程.pdf](https://pan.baidu.com/s/10iFqDOHSveJC3VC7dl1vMw) -- [Effective C++.pdf](https://pan.baidu.com/s/1o-hgLJ4XvXAHeFhWAuuiFQ) - -# 工具 - -- [Pro Git.pdf](https://pan.baidu.com/s/1zYoS3lB1yCCT-So1YeoRuA) -- [正则表达式必知必会.pdf](https://pan.baidu.com/s/1ybA1qvjx4p844Pd8zDlx7Q) - -# 编码实践 - -- [代码大全.pdf](https://pan.baidu.com/s/1H1ilY54BISk7oDaKYpcrwA) -- [重构.pdf](https://pan.baidu.com/s/1pWGwRRVxtpSmlsK7B1uU7Q) -- [敏捷软件开发.pdf](https://pan.baidu.com/s/1HGHeahqtscz7iczhK7ps-Q) -- [编写可读代码的艺术.pdf](https://pan.baidu.com/s/14uxNIdeXKLOnUJ6LMRndPg) -- [程序员的职业素养.pdf](https://pan.baidu.com/s/1MaNeNsoqlTMn2uuT1QrsHQ) -- [人月神话.pdf](https://pan.baidu.com/s/17sIRZxCf_uJMZNnqAHEDkA) -- [黑客与画家.pdf](https://pan.baidu.com/s/1s0vhcWxN_36PpZeJoOHrKA) - -# 科普 - -- [计算机程序的构造与解释.pdf](https://pan.baidu.com/s/1fKo7ntvQUettvjaTQqyCEw) -- [数学之美.pdf](https://pan.baidu.com/s/1dNFZcBdDhA80-pWT1qcQSg) -- [编码.pdf](https://pan.baidu.com/s/1fII84UPuo8aIxDkOakvUVg) -- [编程珠玑.pdf](https://pan.baidu.com/s/1XarJowXrxoBtKdmVCGcm1w) diff --git a/other/handbook.png b/other/handbook.png deleted file mode 100644 index 68deb247..00000000 Binary files a/other/handbook.png and /dev/null differ diff --git a/other/postface.md b/other/postface.md deleted file mode 100644 index 0a3918d9..00000000 --- a/other/postface.md +++ /dev/null @@ -1,29 +0,0 @@ -# 关于仓库 - -本仓库是笔者在准备 2018 年春招实习过程中的学习总结,内容以计算机书籍的学习笔记为主,在整理重点知识的同时会尽量保证知识的系统性。 - -# 关于贡献 - -因为大部分内容是笔者一个字一个字打上去的,所有难免会有一些笔误。如果发现,可以直接在相应的文档上编辑修改。 - -笔者能力有限,很多内容还不够完善。如果您希望和笔者一起完善这个仓库,可以在发表一个 Issue,表明您想要添加的内容,笔者会及时查看。 - -因为不打算将这个仓库做成一个大而全的面试宝典,因此只希望添加一些比较通用的基础知识,或者是和 Java 与分布式相关的内容,但是不添加 Java Web 相关的内容。 - -您也可以在 Issues 中发表一些关于改进本仓库的反馈意见。 - -# 关于上传 - -笔者在本地使用为知笔记软件进行书写,为了方便将本地笔记内容上传到 Github 上,实现了一整套自动化上传方案,包括文本文件的导出、提取图片、Markdown 文档转换、Git 同步。 - -进行 Markdown 文档转换的原因是 Github 使用的 GFM 不支持 MathJax 公式,也不支持 TOC 标记,因此需要替换 MathJax 公式为 CodeCogs 的云服务和重新生成 TOC 目录。这里提供了笔者实现的 GFM 文档转换工具的下载:[GFM-Converter](https://github.com/CyC2018/GFM-Converter)。 - -# 关于排版 - -笔记内容按照 [中文文案排版指北](http://mazhuang.org/wiki/chinese-copywriting-guidelines/#%E4%B8%8D%E8%A6%81%E4%BD%BF%E7%94%A8%E4%B8%8D%E5%9C%B0%E9%81%93%E7%9A%84%E7%BC%A9%E5%86%99) 进行排版,以保证内容的可读性。这里提供了笔者实现的中英混排文档在线工具:[Text-Typesetting](https://github.com/CyC2018/Markdown-Typesetting),目前实现了加空格的功能,之后打算实现对英文专有名词提示首字母大写的功能。 - -不使用 `![]()` 这种方式来引用图片的原因是为了能够控制图片以合适的大小显示,并且在 GFM 中无法使用 `
![]()
` 让图片居中显示,只能使用 `
` 达到此目的。 - -# 关于转载 - -本仓库内容使用到的资料都会在最后面的参考资料中给出引用链接,希望您在使用本仓库的内容时也能给出相应的引用链接。 diff --git a/other/s10328621.jpg b/other/s10328621.jpg deleted file mode 100644 index e7f71386..00000000 Binary files a/other/s10328621.jpg and /dev/null differ diff --git a/other/s1074361.jpg b/other/s1074361.jpg deleted file mode 100644 index d29002aa..00000000 Binary files a/other/s1074361.jpg and /dev/null differ diff --git a/other/s1086045.jpg b/other/s1086045.jpg deleted file mode 100644 index cff87033..00000000 Binary files a/other/s1086045.jpg and /dev/null differ diff --git a/other/s1092076.jpg b/other/s1092076.jpg deleted file mode 100644 index e4b15eb9..00000000 Binary files a/other/s1092076.jpg and /dev/null differ diff --git a/other/s1113106.jpg b/other/s1113106.jpg deleted file mode 100644 index c410ffce..00000000 Binary files a/other/s1113106.jpg and /dev/null differ diff --git a/other/s11194203.jpg b/other/s11194203.jpg deleted file mode 100644 index b573c8c7..00000000 Binary files a/other/s11194203.jpg and /dev/null differ diff --git a/other/s1495029.jpg b/other/s1495029.jpg deleted file mode 100644 index aa7b0e42..00000000 Binary files a/other/s1495029.jpg and /dev/null differ diff --git a/other/s1613283.jpg b/other/s1613283.jpg deleted file mode 100644 index 3a289bab..00000000 Binary files a/other/s1613283.jpg and /dev/null differ diff --git a/other/s1650904.jpg b/other/s1650904.jpg deleted file mode 100644 index c2f342a9..00000000 Binary files a/other/s1650904.jpg and /dev/null differ diff --git a/other/s1671095.jpg b/other/s1671095.jpg deleted file mode 100644 index 55374e82..00000000 Binary files a/other/s1671095.jpg and /dev/null differ diff --git a/other/s2359163.jpg b/other/s2359163.jpg deleted file mode 100644 index 81a20334..00000000 Binary files a/other/s2359163.jpg and /dev/null differ diff --git a/other/s26676928.jpg b/other/s26676928.jpg deleted file mode 100644 index cfabfb6f..00000000 Binary files a/other/s26676928.jpg and /dev/null differ diff --git a/other/s2686916.jpg b/other/s2686916.jpg deleted file mode 100644 index 560eaa00..00000000 Binary files a/other/s2686916.jpg and /dev/null differ diff --git a/other/s27023182.jpg b/other/s27023182.jpg deleted file mode 100644 index d2ef9781..00000000 Binary files a/other/s27023182.jpg and /dev/null differ diff --git a/other/s27043456.jpg b/other/s27043456.jpg deleted file mode 100644 index 5ca8840d..00000000 Binary files a/other/s27043456.jpg and /dev/null differ diff --git a/other/s27243455.jpg b/other/s27243455.jpg deleted file mode 100644 index c08e0915..00000000 Binary files a/other/s27243455.jpg and /dev/null differ diff --git a/other/s27283822.jpg b/other/s27283822.jpg deleted file mode 100644 index d01547a4..00000000 Binary files a/other/s27283822.jpg and /dev/null differ diff --git a/other/s27297117.jpg b/other/s27297117.jpg deleted file mode 100644 index 5c33d895..00000000 Binary files a/other/s27297117.jpg and /dev/null differ diff --git a/other/s27458236.jpg b/other/s27458236.jpg deleted file mode 100644 index 2263ce6a..00000000 Binary files a/other/s27458236.jpg and /dev/null differ diff --git a/other/s2794811.jpg b/other/s2794811.jpg deleted file mode 100644 index b0a3addf..00000000 Binary files a/other/s2794811.jpg and /dev/null differ diff --git a/other/s28296984.jpg b/other/s28296984.jpg deleted file mode 100644 index 9207c218..00000000 Binary files a/other/s28296984.jpg and /dev/null differ diff --git a/other/s28313721.jpg b/other/s28313721.jpg deleted file mode 100644 index 6a466789..00000000 Binary files a/other/s28313721.jpg and /dev/null differ diff --git a/other/s28322244.jpg b/other/s28322244.jpg deleted file mode 100644 index dad9199a..00000000 Binary files a/other/s28322244.jpg and /dev/null differ diff --git a/other/s28341985.jpg b/other/s28341985.jpg deleted file mode 100644 index 5353a050..00000000 Binary files a/other/s28341985.jpg and /dev/null differ diff --git a/other/s28845534.jpg b/other/s28845534.jpg deleted file mode 100644 index db2cebef..00000000 Binary files a/other/s28845534.jpg and /dev/null differ diff --git a/other/s29195878.jpg b/other/s29195878.jpg deleted file mode 100644 index 3846011c..00000000 Binary files a/other/s29195878.jpg and /dev/null differ diff --git a/other/s2992671.jpg b/other/s2992671.jpg deleted file mode 100644 index 14ae2d75..00000000 Binary files a/other/s2992671.jpg and /dev/null differ diff --git a/other/s2996168.jpg b/other/s2996168.jpg deleted file mode 100644 index 57c03982..00000000 Binary files a/other/s2996168.jpg and /dev/null differ diff --git a/other/s3296854.jpg b/other/s3296854.jpg deleted file mode 100644 index de15b190..00000000 Binary files a/other/s3296854.jpg and /dev/null differ diff --git a/other/s3895413.jpg b/other/s3895413.jpg deleted file mode 100644 index 2c9bb790..00000000 Binary files a/other/s3895413.jpg and /dev/null differ diff --git a/other/s4141593.jpg b/other/s4141593.jpg deleted file mode 100644 index bbb8eb76..00000000 Binary files a/other/s4141593.jpg and /dev/null differ diff --git a/other/s4157180.jpg b/other/s4157180.jpg deleted file mode 100644 index cd33ffad..00000000 Binary files a/other/s4157180.jpg and /dev/null differ diff --git a/other/s4245786.jpg b/other/s4245786.jpg deleted file mode 100644 index d9ae661a..00000000 Binary files a/other/s4245786.jpg and /dev/null differ diff --git a/other/s4379914.jpg b/other/s4379914.jpg deleted file mode 100644 index fbc8da34..00000000 Binary files a/other/s4379914.jpg and /dev/null differ diff --git a/other/s4399937.jpg b/other/s4399937.jpg deleted file mode 100644 index 74984e17..00000000 Binary files a/other/s4399937.jpg and /dev/null differ diff --git a/other/s4436543.jpg b/other/s4436543.jpg deleted file mode 100644 index acdd01bd..00000000 Binary files a/other/s4436543.jpg and /dev/null differ diff --git a/other/s4510534.jpg b/other/s4510534.jpg deleted file mode 100644 index 331b991f..00000000 Binary files a/other/s4510534.jpg and /dev/null differ diff --git a/other/s4647091.jpg b/other/s4647091.jpg deleted file mode 100644 index 277c7ea9..00000000 Binary files a/other/s4647091.jpg and /dev/null differ diff --git a/other/s4669554.jpg b/other/s4669554.jpg deleted file mode 100644 index 2864ba52..00000000 Binary files a/other/s4669554.jpg and /dev/null differ diff --git a/other/s4687321.jpg b/other/s4687321.jpg deleted file mode 100644 index 63d61b1a..00000000 Binary files a/other/s4687321.jpg and /dev/null differ diff --git a/other/s5968156.jpg b/other/s5968156.jpg deleted file mode 100644 index e8189c41..00000000 Binary files a/other/s5968156.jpg and /dev/null differ diff --git a/other/s7038106.jpg b/other/s7038106.jpg deleted file mode 100644 index fed7fab0..00000000 Binary files a/other/s7038106.jpg and /dev/null differ diff --git a/other/s7663093.jpg b/other/s7663093.jpg deleted file mode 100644 index 8d352197..00000000 Binary files a/other/s7663093.jpg and /dev/null differ diff --git a/other/s8938479.jpg b/other/s8938479.jpg deleted file mode 100644 index d79e664c..00000000 Binary files a/other/s8938479.jpg and /dev/null differ diff --git a/other/s9114855.jpg b/other/s9114855.jpg deleted file mode 100644 index 861d1cec..00000000 Binary files a/other/s9114855.jpg and /dev/null differ diff --git a/pics/0042edad-8e3b-4279-bd93-6906fcd1b640.jpg b/pics/0042edad-8e3b-4279-bd93-6906fcd1b640.jpg deleted file mode 100644 index 9cabf716..00000000 Binary files a/pics/0042edad-8e3b-4279-bd93-6906fcd1b640.jpg and /dev/null differ diff --git a/pics/004edd56-1546-4052-a7f9-a9f7895ccec5.png b/pics/004edd56-1546-4052-a7f9-a9f7895ccec5.png new file mode 100644 index 00000000..539a6c22 Binary files /dev/null and b/pics/004edd56-1546-4052-a7f9-a9f7895ccec5.png differ diff --git a/pics/005d83c2-e64a-41f0-bbdd-51c71d494a18.jpg b/pics/005d83c2-e64a-41f0-bbdd-51c71d494a18.jpg deleted file mode 100644 index 00ac9106..00000000 Binary files a/pics/005d83c2-e64a-41f0-bbdd-51c71d494a18.jpg and /dev/null differ diff --git a/pics/00d8d345-cd4a-48af-919e-209d2788eca7.jpg b/pics/00d8d345-cd4a-48af-919e-209d2788eca7.jpg deleted file mode 100644 index f20c6cdc..00000000 Binary files a/pics/00d8d345-cd4a-48af-919e-209d2788eca7.jpg and /dev/null differ diff --git a/pics/0126ff14-d52d-4a6e-b8ca-e429881e23b7.png b/pics/0126ff14-d52d-4a6e-b8ca-e429881e23b7.png deleted file mode 100644 index f633c0e9..00000000 Binary files a/pics/0126ff14-d52d-4a6e-b8ca-e429881e23b7.png and /dev/null differ diff --git a/pics/01658047-0d86-4a7a-a8ca-7ea20fa1fdde.png b/pics/01658047-0d86-4a7a-a8ca-7ea20fa1fdde.png deleted file mode 100644 index e459e145..00000000 Binary files a/pics/01658047-0d86-4a7a-a8ca-7ea20fa1fdde.png and /dev/null differ diff --git a/pics/02986f62-c641-44a8-a55f-983581490e0c.png b/pics/02986f62-c641-44a8-a55f-983581490e0c.png new file mode 100644 index 00000000..96726ebc Binary files /dev/null and b/pics/02986f62-c641-44a8-a55f-983581490e0c.png differ diff --git a/pics/037f84c6-b470-42e0-a2d9-fd8cd7e96fa8.png b/pics/037f84c6-b470-42e0-a2d9-fd8cd7e96fa8.png deleted file mode 100644 index 11ca467d..00000000 Binary files a/pics/037f84c6-b470-42e0-a2d9-fd8cd7e96fa8.png and /dev/null differ diff --git a/pics/04662fa2-d19b-4de3-a829-50acb7af75d7.png b/pics/04662fa2-d19b-4de3-a829-50acb7af75d7.png deleted file mode 100644 index 18c04f3b..00000000 Binary files a/pics/04662fa2-d19b-4de3-a829-50acb7af75d7.png and /dev/null differ diff --git a/pics/04ff7ae6-7bee-4cf8-82f8-dfe2ba1f3616.jpg b/pics/04ff7ae6-7bee-4cf8-82f8-dfe2ba1f3616.jpg deleted file mode 100644 index 44529eab..00000000 Binary files a/pics/04ff7ae6-7bee-4cf8-82f8-dfe2ba1f3616.jpg and /dev/null differ diff --git a/pics/051760ba-e658-401f-9a1c-15adcb405191.png b/pics/051760ba-e658-401f-9a1c-15adcb405191.png deleted file mode 100644 index 8c50f3be..00000000 Binary files a/pics/051760ba-e658-401f-9a1c-15adcb405191.png and /dev/null differ diff --git a/pics/054d8d16-a3f9-460d-a365-834ba9940e3b.jpg b/pics/054d8d16-a3f9-460d-a365-834ba9940e3b.jpg deleted file mode 100644 index 2a14a2eb..00000000 Binary files a/pics/054d8d16-a3f9-460d-a365-834ba9940e3b.jpg and /dev/null differ diff --git a/pics/05e41947-3cbc-4f02-a428-96765ec916ff.png b/pics/05e41947-3cbc-4f02-a428-96765ec916ff.png new file mode 100644 index 00000000..d15370b4 Binary files /dev/null and b/pics/05e41947-3cbc-4f02-a428-96765ec916ff.png differ diff --git a/pics/0635cbe8.png b/pics/0635cbe8.png new file mode 100644 index 00000000..849c9eaf Binary files /dev/null and b/pics/0635cbe8.png differ diff --git a/pics/065c3bbb-3ea0-4dbf-8f26-01d0e0ba7db7.png b/pics/065c3bbb-3ea0-4dbf-8f26-01d0e0ba7db7.png deleted file mode 100644 index cf80e5ae..00000000 Binary files a/pics/065c3bbb-3ea0-4dbf-8f26-01d0e0ba7db7.png and /dev/null differ diff --git a/pics/080f488c-75ef-49a8-a49d-78fa372ad422.png b/pics/080f488c-75ef-49a8-a49d-78fa372ad422.png deleted file mode 100644 index c40ea2d2..00000000 Binary files a/pics/080f488c-75ef-49a8-a49d-78fa372ad422.png and /dev/null differ diff --git a/pics/08427d38-8df1-49a1-8990-e0ce5ee36ca2.png b/pics/08427d38-8df1-49a1-8990-e0ce5ee36ca2.png new file mode 100644 index 00000000..13d49836 Binary files /dev/null and b/pics/08427d38-8df1-49a1-8990-e0ce5ee36ca2.png differ diff --git a/pics/086871db-5871-460f-97b7-126cd738bb0e.jpg b/pics/086871db-5871-460f-97b7-126cd738bb0e.jpg deleted file mode 100644 index 75ce9011..00000000 Binary files a/pics/086871db-5871-460f-97b7-126cd738bb0e.jpg and /dev/null differ diff --git a/pics/08738dd0-ae8e-404a-ba78-a6b1b7d225b3.jpg b/pics/08738dd0-ae8e-404a-ba78-a6b1b7d225b3.jpg deleted file mode 100644 index 7aadcbe1..00000000 Binary files a/pics/08738dd0-ae8e-404a-ba78-a6b1b7d225b3.jpg and /dev/null differ diff --git a/pics/095720ee-84b3-42ff-af71-70ceb6a2f4a3.png b/pics/095720ee-84b3-42ff-af71-70ceb6a2f4a3.png new file mode 100644 index 00000000..57e4309b Binary files /dev/null and b/pics/095720ee-84b3-42ff-af71-70ceb6a2f4a3.png differ diff --git a/pics/09e398d8-9c6e-48f6-b48b-8b4f9de61d1d.png b/pics/09e398d8-9c6e-48f6-b48b-8b4f9de61d1d.png deleted file mode 100644 index 908ebe92..00000000 Binary files a/pics/09e398d8-9c6e-48f6-b48b-8b4f9de61d1d.png and /dev/null differ diff --git a/pics/0a9f4125-b6ab-4e94-a807-fd7070ae726a.png b/pics/0a9f4125-b6ab-4e94-a807-fd7070ae726a.png new file mode 100644 index 00000000..395d9201 Binary files /dev/null and b/pics/0a9f4125-b6ab-4e94-a807-fd7070ae726a.png differ diff --git a/pics/0b304499-0d7c-49cc-b784-3e7a805c9fba.jpg b/pics/0b304499-0d7c-49cc-b784-3e7a805c9fba.jpg deleted file mode 100644 index 2a77e0c3..00000000 Binary files a/pics/0b304499-0d7c-49cc-b784-3e7a805c9fba.jpg and /dev/null differ diff --git a/pics/0c55e11c-d3ce-4cd8-b139-028aea6f40e3.png b/pics/0c55e11c-d3ce-4cd8-b139-028aea6f40e3.png new file mode 100644 index 00000000..5892c1f9 Binary files /dev/null and b/pics/0c55e11c-d3ce-4cd8-b139-028aea6f40e3.png differ diff --git a/pics/0c723f4c-e13d-42f7-aeb2-1f160c7cc4b6.png b/pics/0c723f4c-e13d-42f7-aeb2-1f160c7cc4b6.png deleted file mode 100644 index 3252d0af..00000000 Binary files a/pics/0c723f4c-e13d-42f7-aeb2-1f160c7cc4b6.png and /dev/null differ diff --git a/pics/0dd97d9e-7a38-460e-a2ac-3e4511145240.png b/pics/0dd97d9e-7a38-460e-a2ac-3e4511145240.png deleted file mode 100644 index 8a3e23d0..00000000 Binary files a/pics/0dd97d9e-7a38-460e-a2ac-3e4511145240.png and /dev/null differ diff --git a/pics/0ddebc5c-7c24-46b1-98db-4fa5e54db16b.png b/pics/0ddebc5c-7c24-46b1-98db-4fa5e54db16b.png deleted file mode 100644 index 7fb917af..00000000 Binary files a/pics/0ddebc5c-7c24-46b1-98db-4fa5e54db16b.png and /dev/null differ diff --git a/pics/0de18cdb-e974-47a3-af47-9538edafe857.png b/pics/0de18cdb-e974-47a3-af47-9538edafe857.png deleted file mode 100644 index ba797ed8..00000000 Binary files a/pics/0de18cdb-e974-47a3-af47-9538edafe857.png and /dev/null differ diff --git a/pics/0e34263d-7287-4ffe-a716-37c53d1a2526.png b/pics/0e34263d-7287-4ffe-a716-37c53d1a2526.png new file mode 100644 index 00000000..c5f0bbe3 Binary files /dev/null and b/pics/0e34263d-7287-4ffe-a716-37c53d1a2526.png differ diff --git a/pics/0e4c8a7f-f84c-4c4e-9544-49cd40167af8.png b/pics/0e4c8a7f-f84c-4c4e-9544-49cd40167af8.png deleted file mode 100644 index 6caf2f25..00000000 Binary files a/pics/0e4c8a7f-f84c-4c4e-9544-49cd40167af8.png and /dev/null differ diff --git a/pics/0e6cf8bd-b84e-4b3c-b79d-40d7dd54e120.png b/pics/0e6cf8bd-b84e-4b3c-b79d-40d7dd54e120.png deleted file mode 100644 index 5d13a325..00000000 Binary files a/pics/0e6cf8bd-b84e-4b3c-b79d-40d7dd54e120.png and /dev/null differ diff --git a/pics/0ed83061-9c1e-4df3-b15b-69aad5bfe9b8.png b/pics/0ed83061-9c1e-4df3-b15b-69aad5bfe9b8.png deleted file mode 100644 index f4e07915..00000000 Binary files a/pics/0ed83061-9c1e-4df3-b15b-69aad5bfe9b8.png and /dev/null differ diff --git a/pics/0f31bc7a-d60b-48a6-8e3f-597708369e52.png b/pics/0f31bc7a-d60b-48a6-8e3f-597708369e52.png deleted file mode 100644 index df191c91..00000000 Binary files a/pics/0f31bc7a-d60b-48a6-8e3f-597708369e52.png and /dev/null differ diff --git a/pics/0f373947-c68f-45b4-a59e-086154745ac5.png b/pics/0f373947-c68f-45b4-a59e-086154745ac5.png new file mode 100644 index 00000000..6b474d85 Binary files /dev/null and b/pics/0f373947-c68f-45b4-a59e-086154745ac5.png differ diff --git a/pics/0f39c274-b79c-4e83-8c7c-94fc2747832d.jpg b/pics/0f39c274-b79c-4e83-8c7c-94fc2747832d.jpg deleted file mode 100644 index 8cdf9af3..00000000 Binary files a/pics/0f39c274-b79c-4e83-8c7c-94fc2747832d.jpg and /dev/null differ diff --git a/pics/0f6fe85a-b680-47ea-af67-21ab98a62f8c.jpg b/pics/0f6fe85a-b680-47ea-af67-21ab98a62f8c.jpg deleted file mode 100644 index 502722af..00000000 Binary files a/pics/0f6fe85a-b680-47ea-af67-21ab98a62f8c.jpg and /dev/null differ diff --git a/pics/0f86eb11-3724-48de-9f27-499dfc7e96f1.png b/pics/0f86eb11-3724-48de-9f27-499dfc7e96f1.png deleted file mode 100644 index defa899e..00000000 Binary files a/pics/0f86eb11-3724-48de-9f27-499dfc7e96f1.png and /dev/null differ diff --git a/pics/0f8c0a60-d4c6-47f4-978d-1a5c393fedac.jpg b/pics/0f8c0a60-d4c6-47f4-978d-1a5c393fedac.jpg deleted file mode 100644 index 4719d5ae..00000000 Binary files a/pics/0f8c0a60-d4c6-47f4-978d-1a5c393fedac.jpg and /dev/null differ diff --git a/pics/1005dc9d-9049-4b06-9524-6171e56ebd8c.png b/pics/1005dc9d-9049-4b06-9524-6171e56ebd8c.png deleted file mode 100644 index 04142289..00000000 Binary files a/pics/1005dc9d-9049-4b06-9524-6171e56ebd8c.png and /dev/null differ diff --git a/pics/106f5585-b2e7-4718-be5d-3b322d1ef42a.jpg b/pics/106f5585-b2e7-4718-be5d-3b322d1ef42a.jpg deleted file mode 100644 index b70e5806..00000000 Binary files a/pics/106f5585-b2e7-4718-be5d-3b322d1ef42a.jpg and /dev/null differ diff --git a/pics/107a6a2b-f15b-4cad-bced-b7fb95258c9c.png b/pics/107a6a2b-f15b-4cad-bced-b7fb95258c9c.png new file mode 100644 index 00000000..35c2ddb1 Binary files /dev/null and b/pics/107a6a2b-f15b-4cad-bced-b7fb95258c9c.png differ diff --git a/pics/10bdf7bf-0daa-4a26-b927-f142b3f8e72b.png b/pics/10bdf7bf-0daa-4a26-b927-f142b3f8e72b.png new file mode 100644 index 00000000..cb78d856 Binary files /dev/null and b/pics/10bdf7bf-0daa-4a26-b927-f142b3f8e72b.png differ diff --git a/pics/10f5e35b-1c71-4717-9e80-47f259702642.jpg b/pics/10f5e35b-1c71-4717-9e80-47f259702642.jpg new file mode 100644 index 00000000..7041fd5f Binary files /dev/null and b/pics/10f5e35b-1c71-4717-9e80-47f259702642.jpg differ diff --git a/pics/110b1a9b-87cd-45c3-a21d-824623715b33.jpg b/pics/110b1a9b-87cd-45c3-a21d-824623715b33.jpg deleted file mode 100644 index 7b7359ee..00000000 Binary files a/pics/110b1a9b-87cd-45c3-a21d-824623715b33.jpg and /dev/null differ diff --git a/pics/114c49a6-72e3-4264-ae07-c564127094ac.png b/pics/114c49a6-72e3-4264-ae07-c564127094ac.png deleted file mode 100644 index 8cb30058..00000000 Binary files a/pics/114c49a6-72e3-4264-ae07-c564127094ac.png and /dev/null differ diff --git a/pics/12b458dd-526d-46e2-acca-cf3b501d580e.png b/pics/12b458dd-526d-46e2-acca-cf3b501d580e.png deleted file mode 100644 index 7f671c19..00000000 Binary files a/pics/12b458dd-526d-46e2-acca-cf3b501d580e.png and /dev/null differ diff --git a/pics/144d28a0-1dc5-4aba-8961-ced5bc88428a.jpg b/pics/144d28a0-1dc5-4aba-8961-ced5bc88428a.jpg deleted file mode 100644 index e3b4b466..00000000 Binary files a/pics/144d28a0-1dc5-4aba-8961-ced5bc88428a.jpg and /dev/null differ diff --git a/pics/14583c71-8f57-4939-a9fc-065469b1bb7a.png b/pics/14583c71-8f57-4939-a9fc-065469b1bb7a.png deleted file mode 100644 index 0ea69d17..00000000 Binary files a/pics/14583c71-8f57-4939-a9fc-065469b1bb7a.png and /dev/null differ diff --git a/pics/1556770b-8c01-4681-af10-46f1df69202c.jpg b/pics/1556770b-8c01-4681-af10-46f1df69202c.jpg new file mode 100644 index 00000000..94d1adc2 Binary files /dev/null and b/pics/1556770b-8c01-4681-af10-46f1df69202c.jpg differ diff --git a/pics/15e1edf0-8908-4815-af5e-a74e456da23b.png b/pics/15e1edf0-8908-4815-af5e-a74e456da23b.png deleted file mode 100644 index 90877219..00000000 Binary files a/pics/15e1edf0-8908-4815-af5e-a74e456da23b.png and /dev/null differ diff --git a/pics/163cf8b4-5f30-46c9-af00-316a71b3c890.jpg b/pics/163cf8b4-5f30-46c9-af00-316a71b3c890.jpg deleted file mode 100644 index b00c7858..00000000 Binary files a/pics/163cf8b4-5f30-46c9-af00-316a71b3c890.jpg and /dev/null differ diff --git a/pics/1706ce58-a081-4fed-9b36-c3c0d7e22b3a.jpg b/pics/1706ce58-a081-4fed-9b36-c3c0d7e22b3a.jpg deleted file mode 100644 index 4f24f5bd..00000000 Binary files a/pics/1706ce58-a081-4fed-9b36-c3c0d7e22b3a.jpg and /dev/null differ diff --git a/pics/17d807ef-03bf-4824-a97c-ea5fb58ec61d.jpg b/pics/17d807ef-03bf-4824-a97c-ea5fb58ec61d.jpg deleted file mode 100644 index 2f9426b0..00000000 Binary files a/pics/17d807ef-03bf-4824-a97c-ea5fb58ec61d.jpg and /dev/null differ diff --git a/pics/185b9c49-4c13-4241-a848-fbff85c03a64.png b/pics/185b9c49-4c13-4241-a848-fbff85c03a64.png new file mode 100644 index 00000000..6f56bc5e Binary files /dev/null and b/pics/185b9c49-4c13-4241-a848-fbff85c03a64.png differ diff --git a/pics/1984a822-da4e-4461-b826-55b798e2d419.jpg b/pics/1984a822-da4e-4461-b826-55b798e2d419.jpg deleted file mode 100644 index 04eda35a..00000000 Binary files a/pics/1984a822-da4e-4461-b826-55b798e2d419.jpg and /dev/null differ diff --git a/pics/19f2c9ef-6739-4a95-8e9d-aa3f7654e028.jpg b/pics/19f2c9ef-6739-4a95-8e9d-aa3f7654e028.jpg deleted file mode 100644 index 905b16ba..00000000 Binary files a/pics/19f2c9ef-6739-4a95-8e9d-aa3f7654e028.jpg and /dev/null differ diff --git a/pics/1a511c76-bb6b-40ab-b8aa-39eeb619d673.jpg b/pics/1a511c76-bb6b-40ab-b8aa-39eeb619d673.jpg deleted file mode 100644 index 4d753f51..00000000 Binary files a/pics/1a511c76-bb6b-40ab-b8aa-39eeb619d673.jpg and /dev/null differ diff --git a/pics/1ab49e39-012b-4383-8284-26570987e3c4.jpg b/pics/1ab49e39-012b-4383-8284-26570987e3c4.jpg new file mode 100644 index 00000000..48a91211 Binary files /dev/null and b/pics/1ab49e39-012b-4383-8284-26570987e3c4.jpg differ diff --git a/pics/1b4d6737-d834-46ed-8f9d-6f123e29c8dd.jpg b/pics/1b4d6737-d834-46ed-8f9d-6f123e29c8dd.jpg deleted file mode 100644 index 0f5ecab9..00000000 Binary files a/pics/1b4d6737-d834-46ed-8f9d-6f123e29c8dd.jpg and /dev/null differ diff --git a/pics/1bfa3118-f3cd-4480-a950-cf6d646015db.png b/pics/1bfa3118-f3cd-4480-a950-cf6d646015db.png new file mode 100644 index 00000000..c28fe627 Binary files /dev/null and b/pics/1bfa3118-f3cd-4480-a950-cf6d646015db.png differ diff --git a/pics/1c012d74-6b9d-4f25-a016-7ad4f1f1521898780376.png b/pics/1c012d74-6b9d-4f25-a016-7ad4f1f1521898780376.png deleted file mode 100644 index 0c959d2a..00000000 Binary files a/pics/1c012d74-6b9d-4f25-a016-7ad4f1f1521898780376.png and /dev/null differ diff --git a/pics/1c012d74-6b9d-4f25-a016-7ad4f1fdeee1.png b/pics/1c012d74-6b9d-4f25-a016-7ad4f1fdeee1.png deleted file mode 100644 index 0c959d2a..00000000 Binary files a/pics/1c012d74-6b9d-4f25-a016-7ad4f1fdeee1.png and /dev/null differ diff --git a/pics/1c237399-e322-4930-b5b4-a582b1ad8bda.png b/pics/1c237399-e322-4930-b5b4-a582b1ad8bda.png deleted file mode 100644 index aa8847fb..00000000 Binary files a/pics/1c237399-e322-4930-b5b4-a582b1ad8bda.png and /dev/null differ diff --git a/pics/1c8432c8-2552-457f-b117-1da36c697221.jpg b/pics/1c8432c8-2552-457f-b117-1da36c697221.jpg deleted file mode 100644 index f92174fb..00000000 Binary files a/pics/1c8432c8-2552-457f-b117-1da36c697221.jpg and /dev/null differ diff --git a/pics/1c8ccf5c-7ecd-4b8a-b160-3f72a510ce26.png b/pics/1c8ccf5c-7ecd-4b8a-b160-3f72a510ce26.png deleted file mode 100644 index 69ce5de1..00000000 Binary files a/pics/1c8ccf5c-7ecd-4b8a-b160-3f72a510ce26.png and /dev/null differ diff --git a/pics/1dc481cc-99f6-4fa8-8f68-fbd563995bf5.png b/pics/1dc481cc-99f6-4fa8-8f68-fbd563995bf5.png deleted file mode 100644 index 4723269f..00000000 Binary files a/pics/1dc481cc-99f6-4fa8-8f68-fbd563995bf5.png and /dev/null differ diff --git a/pics/1dc67ff6-d29b-4864-baac-fd6b23f9b2ac.png b/pics/1dc67ff6-d29b-4864-baac-fd6b23f9b2ac.png deleted file mode 100644 index 014e215c..00000000 Binary files a/pics/1dc67ff6-d29b-4864-baac-fd6b23f9b2ac.png and /dev/null differ diff --git a/pics/1dd56e61-2970-4d27-97c2-6e81cee86978.jpg b/pics/1dd56e61-2970-4d27-97c2-6e81cee86978.jpg deleted file mode 100644 index 8e56f63a..00000000 Binary files a/pics/1dd56e61-2970-4d27-97c2-6e81cee86978.jpg and /dev/null differ diff --git a/pics/1e09d75f-6268-4425-acf8-8ecd1b4a0ef3.jpg b/pics/1e09d75f-6268-4425-acf8-8ecd1b4a0ef3.jpg deleted file mode 100644 index 95e3b7ee..00000000 Binary files a/pics/1e09d75f-6268-4425-acf8-8ecd1b4a0ef3.jpg and /dev/null differ diff --git a/pics/1ea4dc9a-c4dd-46b5-bb11-49f98d57ded1.png b/pics/1ea4dc9a-c4dd-46b5-bb11-49f98d57ded1.png deleted file mode 100644 index 71ed45ee..00000000 Binary files a/pics/1ea4dc9a-c4dd-46b5-bb11-49f98d57ded1.png and /dev/null differ diff --git a/pics/1f039a45-6b91-4f31-a2c2-6c63eb8bdb56.png b/pics/1f039a45-6b91-4f31-a2c2-6c63eb8bdb56.png new file mode 100644 index 00000000..eb4daeab Binary files /dev/null and b/pics/1f039a45-6b91-4f31-a2c2-6c63eb8bdb56.png differ diff --git a/pics/2017511e-22f0-4d74-873d-1261b71cf5a4.png b/pics/2017511e-22f0-4d74-873d-1261b71cf5a4.png new file mode 100644 index 00000000..81633b51 Binary files /dev/null and b/pics/2017511e-22f0-4d74-873d-1261b71cf5a4.png differ diff --git a/pics/202cd47c-ef2f-4c0e-a511-53359553387d.png b/pics/202cd47c-ef2f-4c0e-a511-53359553387d.png deleted file mode 100644 index 25e0d877..00000000 Binary files a/pics/202cd47c-ef2f-4c0e-a511-53359553387d.png and /dev/null differ diff --git a/pics/20368ec9-972e-4d6a-8050-3948334bcda0.jpg b/pics/20368ec9-972e-4d6a-8050-3948334bcda0.jpg deleted file mode 100644 index c6067730..00000000 Binary files a/pics/20368ec9-972e-4d6a-8050-3948334bcda0.jpg and /dev/null differ diff --git a/pics/207c1801-2335-4b1b-b65c-126a0ba966cb.png b/pics/207c1801-2335-4b1b-b65c-126a0ba966cb.png new file mode 100644 index 00000000..cace90b3 Binary files /dev/null and b/pics/207c1801-2335-4b1b-b65c-126a0ba966cb.png differ diff --git a/pics/2093ccfa-e560-44f3-84c7-487d66451708.png b/pics/2093ccfa-e560-44f3-84c7-487d66451708.png deleted file mode 100644 index 9ddb56f2..00000000 Binary files a/pics/2093ccfa-e560-44f3-84c7-487d66451708.png and /dev/null differ diff --git a/pics/20a9ec8c-0c19-4196-96f0-b4a0ea43075d.jpg b/pics/20a9ec8c-0c19-4196-96f0-b4a0ea43075d.jpg deleted file mode 100644 index 6b1a6900..00000000 Binary files a/pics/20a9ec8c-0c19-4196-96f0-b4a0ea43075d.jpg and /dev/null differ diff --git a/pics/21041ec2-babb-483f-bf47-8b8148eec162.png b/pics/21041ec2-babb-483f-bf47-8b8148eec162.png new file mode 100644 index 00000000..7de9b57a Binary files /dev/null and b/pics/21041ec2-babb-483f-bf47-8b8148eec162.png differ diff --git a/pics/21a00b02-c0a6-4bcd-9af0-5ec6bb66e34c.jpg b/pics/21a00b02-c0a6-4bcd-9af0-5ec6bb66e34c.jpg deleted file mode 100644 index 002ded06..00000000 Binary files a/pics/21a00b02-c0a6-4bcd-9af0-5ec6bb66e34c.jpg and /dev/null differ diff --git a/pics/222768a7-914f-4d64-b874-d98f3b926fb6.jpg b/pics/222768a7-914f-4d64-b874-d98f3b926fb6.jpg deleted file mode 100644 index 412af6de..00000000 Binary files a/pics/222768a7-914f-4d64-b874-d98f3b926fb6.jpg and /dev/null differ diff --git a/pics/223fc26e-2fd6-484c-bcb7-443cac134f15.jpg b/pics/223fc26e-2fd6-484c-bcb7-443cac134f15.jpg deleted file mode 100644 index e93bbd01..00000000 Binary files a/pics/223fc26e-2fd6-484c-bcb7-443cac134f15.jpg and /dev/null differ diff --git a/pics/2279cc60-9714-4e0e-aac9-4c348e0c2165.png b/pics/2279cc60-9714-4e0e-aac9-4c348e0c2165.png deleted file mode 100644 index 4bf0a826..00000000 Binary files a/pics/2279cc60-9714-4e0e-aac9-4c348e0c2165.png and /dev/null differ diff --git a/pics/22b39f77-ac47-4978-91ed-84aaf457644c.jpg b/pics/22b39f77-ac47-4978-91ed-84aaf457644c.jpg deleted file mode 100644 index 0bbf51f4..00000000 Binary files a/pics/22b39f77-ac47-4978-91ed-84aaf457644c.jpg and /dev/null differ diff --git a/pics/2366c2ad-5859-4d4e-805f-7e2b88061cd8.jpg b/pics/2366c2ad-5859-4d4e-805f-7e2b88061cd8.jpg deleted file mode 100644 index 744d2c42..00000000 Binary files a/pics/2366c2ad-5859-4d4e-805f-7e2b88061cd8.jpg and /dev/null differ diff --git a/pics/23ba890e-e11c-45e2-a20c-64d217f83430.png b/pics/23ba890e-e11c-45e2-a20c-64d217f83430.png new file mode 100644 index 00000000..5fccbd1c Binary files /dev/null and b/pics/23ba890e-e11c-45e2-a20c-64d217f83430.png differ diff --git a/pics/25226bb2-92cc-40cb-9e7f-c44e79fbb64a.jpg b/pics/25226bb2-92cc-40cb-9e7f-c44e79fbb64a.jpg deleted file mode 100644 index ff506c8b..00000000 Binary files a/pics/25226bb2-92cc-40cb-9e7f-c44e79fbb64a.jpg and /dev/null differ diff --git a/pics/25387681-89f8-4365-a2fa-83b86449ee84.jpg b/pics/25387681-89f8-4365-a2fa-83b86449ee84.jpg deleted file mode 100644 index 89259924..00000000 Binary files a/pics/25387681-89f8-4365-a2fa-83b86449ee84.jpg and /dev/null differ diff --git a/pics/253bd869-ea48-4092-9aed-6906ccb2f3b0.jpg b/pics/253bd869-ea48-4092-9aed-6906ccb2f3b0.jpg deleted file mode 100644 index 42d8c373..00000000 Binary files a/pics/253bd869-ea48-4092-9aed-6906ccb2f3b0.jpg and /dev/null differ diff --git a/pics/2548f2ec-7b00-4ec7-b286-20fc3022e084.jpg b/pics/2548f2ec-7b00-4ec7-b286-20fc3022e084.jpg deleted file mode 100644 index c6d9cd68..00000000 Binary files a/pics/2548f2ec-7b00-4ec7-b286-20fc3022e084.jpg and /dev/null differ diff --git a/pics/26020e1a-06ab-4114-a6b3-e428de690c7e.png b/pics/26020e1a-06ab-4114-a6b3-e428de690c7e.png new file mode 100644 index 00000000..02876968 Binary files /dev/null and b/pics/26020e1a-06ab-4114-a6b3-e428de690c7e.png differ diff --git a/pics/26223561-eea4-463c-8ddb-3bb456c76267.png b/pics/26223561-eea4-463c-8ddb-3bb456c76267.png deleted file mode 100644 index ffd37a22..00000000 Binary files a/pics/26223561-eea4-463c-8ddb-3bb456c76267.png and /dev/null differ diff --git a/pics/265a355d-aead-48aa-b455-f33b62fe729f.png b/pics/265a355d-aead-48aa-b455-f33b62fe729f.png new file mode 100644 index 00000000..bb564f6e Binary files /dev/null and b/pics/265a355d-aead-48aa-b455-f33b62fe729f.png differ diff --git a/pics/26772ecc-a3e3-4ab7-a46f-8b4656990c27.jpg b/pics/26772ecc-a3e3-4ab7-a46f-8b4656990c27.jpg deleted file mode 100644 index 38c0da24..00000000 Binary files a/pics/26772ecc-a3e3-4ab7-a46f-8b4656990c27.jpg and /dev/null differ diff --git a/pics/26cb5e7e-6fa3-44ad-854e-fe24d1a5278c.jpg b/pics/26cb5e7e-6fa3-44ad-854e-fe24d1a5278c.jpg deleted file mode 100644 index 2f7b925b..00000000 Binary files a/pics/26cb5e7e-6fa3-44ad-854e-fe24d1a5278c.jpg and /dev/null differ diff --git a/pics/26ccd069-55ec-4a28-aeb3-025e39e5810f.jpg b/pics/26ccd069-55ec-4a28-aeb3-025e39e5810f.jpg deleted file mode 100644 index 9db84485..00000000 Binary files a/pics/26ccd069-55ec-4a28-aeb3-025e39e5810f.jpg and /dev/null differ diff --git a/pics/2719067e-b299-4639-9065-bed6729dbf0b.png b/pics/2719067e-b299-4639-9065-bed6729dbf0b.png new file mode 100644 index 00000000..95057e05 Binary files /dev/null and b/pics/2719067e-b299-4639-9065-bed6729dbf0b.png differ diff --git a/pics/276c31df-3b28-4ac2-b006-1e80fc86a64f.jpg b/pics/276c31df-3b28-4ac2-b006-1e80fc86a64f.jpg new file mode 100644 index 00000000..8676c440 Binary files /dev/null and b/pics/276c31df-3b28-4ac2-b006-1e80fc86a64f.jpg differ diff --git a/pics/27ace615-558f-4dfb-8ad4-7ac769c10118.jpg b/pics/27ace615-558f-4dfb-8ad4-7ac769c10118.jpg deleted file mode 100644 index 1ae556ba..00000000 Binary files a/pics/27ace615-558f-4dfb-8ad4-7ac769c10118.jpg and /dev/null differ diff --git a/pics/27c2e0b3-8f95-453d-bedc-6398a8566ce9.jpg b/pics/27c2e0b3-8f95-453d-bedc-6398a8566ce9.jpg deleted file mode 100644 index 428a8fd6..00000000 Binary files a/pics/27c2e0b3-8f95-453d-bedc-6398a8566ce9.jpg and /dev/null differ diff --git a/pics/27cd6f0c-f581-45da-b8c9-fed026830560.png b/pics/27cd6f0c-f581-45da-b8c9-fed026830560.png new file mode 100644 index 00000000..40775ac8 Binary files /dev/null and b/pics/27cd6f0c-f581-45da-b8c9-fed026830560.png differ diff --git a/pics/29058e09-bb72-4040-a73d-4c497895e9ce.jpg b/pics/29058e09-bb72-4040-a73d-4c497895e9ce.jpg deleted file mode 100644 index eefed273..00000000 Binary files a/pics/29058e09-bb72-4040-a73d-4c497895e9ce.jpg and /dev/null differ diff --git a/pics/293b9326-02fc-4ad8-8c79-b4a7b5ba60d3.png b/pics/293b9326-02fc-4ad8-8c79-b4a7b5ba60d3.png deleted file mode 100644 index 50900e44..00000000 Binary files a/pics/293b9326-02fc-4ad8-8c79-b4a7b5ba60d3.png and /dev/null differ diff --git a/pics/293d2af9-de1d-403e-bed0-85d029383528.png b/pics/293d2af9-de1d-403e-bed0-85d029383528.png new file mode 100644 index 00000000..36765e32 Binary files /dev/null and b/pics/293d2af9-de1d-403e-bed0-85d029383528.png differ diff --git a/pics/29574e6f-295c-444e-83c7-b162e8a73a83.jpg b/pics/29574e6f-295c-444e-83c7-b162e8a73a83.jpg deleted file mode 100644 index 11ab909d..00000000 Binary files a/pics/29574e6f-295c-444e-83c7-b162e8a73a83.jpg and /dev/null differ diff --git a/pics/299a8cd3-7cfa-45d0-8821-4aa577de4692.png b/pics/299a8cd3-7cfa-45d0-8821-4aa577de4692.png deleted file mode 100644 index 23fe5d4a..00000000 Binary files a/pics/299a8cd3-7cfa-45d0-8821-4aa577de4692.png and /dev/null differ diff --git a/pics/2a398239-ee47-4ea1-b2d8-0ced638839ef.png b/pics/2a398239-ee47-4ea1-b2d8-0ced638839ef.png deleted file mode 100644 index 2290e8e9..00000000 Binary files a/pics/2a398239-ee47-4ea1-b2d8-0ced638839ef.png and /dev/null differ diff --git a/pics/2a40042a-03c8-4556-ad1f-72d89f8c555c.jpg b/pics/2a40042a-03c8-4556-ad1f-72d89f8c555c.jpg deleted file mode 100644 index 605a1f0a..00000000 Binary files a/pics/2a40042a-03c8-4556-ad1f-72d89f8c555c.jpg and /dev/null differ diff --git a/pics/2a502516-5d34-4eef-8f39-916298a60035.png b/pics/2a502516-5d34-4eef-8f39-916298a60035.png deleted file mode 100644 index 3bc4ccb9..00000000 Binary files a/pics/2a502516-5d34-4eef-8f39-916298a60035.png and /dev/null differ diff --git a/pics/2b3410f1-9559-4dd1-bc3d-e3e572247be2.png b/pics/2b3410f1-9559-4dd1-bc3d-e3e572247be2.png deleted file mode 100644 index 9ba05bb5..00000000 Binary files a/pics/2b3410f1-9559-4dd1-bc3d-e3e572247be2.png and /dev/null differ diff --git a/pics/2b6037b2-ec69-4235-ad0e-886fa320d645.jpg b/pics/2b6037b2-ec69-4235-ad0e-886fa320d645.jpg deleted file mode 100644 index 340fc750..00000000 Binary files a/pics/2b6037b2-ec69-4235-ad0e-886fa320d645.jpg and /dev/null differ diff --git a/pics/2c968ec5-0967-49ce-ac06-f8f5c9ab33bc.jpg b/pics/2c968ec5-0967-49ce-ac06-f8f5c9ab33bc.jpg deleted file mode 100644 index c3475c0d..00000000 Binary files a/pics/2c968ec5-0967-49ce-ac06-f8f5c9ab33bc.jpg and /dev/null differ diff --git a/pics/2cdc3ce2-fa82-4c22-baaa-000c07d10473.jpg b/pics/2cdc3ce2-fa82-4c22-baaa-000c07d10473.jpg deleted file mode 100644 index 6e841182..00000000 Binary files a/pics/2cdc3ce2-fa82-4c22-baaa-000c07d10473.jpg and /dev/null differ diff --git a/pics/2d078e08-3a49-46d0-b784-df780b7e4bc3.jpg b/pics/2d078e08-3a49-46d0-b784-df780b7e4bc3.jpg deleted file mode 100644 index 9df4fe3f..00000000 Binary files a/pics/2d078e08-3a49-46d0-b784-df780b7e4bc3.jpg and /dev/null differ diff --git a/pics/2d09a847-b854-439c-9198-b29c65810944.png b/pics/2d09a847-b854-439c-9198-b29c65810944.png new file mode 100644 index 00000000..384f7ef0 Binary files /dev/null and b/pics/2d09a847-b854-439c-9198-b29c65810944.png differ diff --git a/pics/2ddd6132-60be-4a72-9daa-3d9756191f4a.png b/pics/2ddd6132-60be-4a72-9daa-3d9756191f4a.png deleted file mode 100644 index 3590549f..00000000 Binary files a/pics/2ddd6132-60be-4a72-9daa-3d9756191f4a.png and /dev/null differ diff --git a/pics/2e5620c4-b558-46fe-8f12-00c9dd597a61.png b/pics/2e5620c4-b558-46fe-8f12-00c9dd597a61.png deleted file mode 100644 index 5a17489f..00000000 Binary files a/pics/2e5620c4-b558-46fe-8f12-00c9dd597a61.png and /dev/null differ diff --git a/pics/2e6c72f5-3b8e-4e32-b87b-9491322628fe.png b/pics/2e6c72f5-3b8e-4e32-b87b-9491322628fe.png new file mode 100644 index 00000000..701e37f2 Binary files /dev/null and b/pics/2e6c72f5-3b8e-4e32-b87b-9491322628fe.png differ diff --git a/pics/30210b86-472d-4574-abb6-b74898cc17a4.jpg b/pics/30210b86-472d-4574-abb6-b74898cc17a4.jpg new file mode 100644 index 00000000..7726cf8c Binary files /dev/null and b/pics/30210b86-472d-4574-abb6-b74898cc17a4.jpg differ diff --git a/pics/30edea19-3507-423c-bbb0-5184292692d7.png b/pics/30edea19-3507-423c-bbb0-5184292692d7.png deleted file mode 100644 index c99611bb..00000000 Binary files a/pics/30edea19-3507-423c-bbb0-5184292692d7.png and /dev/null differ diff --git a/pics/323ffd6c-8b54-4f3e-b361-555a6c8bf218.png b/pics/323ffd6c-8b54-4f3e-b361-555a6c8bf218.png new file mode 100644 index 00000000..3316254e Binary files /dev/null and b/pics/323ffd6c-8b54-4f3e-b361-555a6c8bf218.png differ diff --git a/pics/3290673d-edab-4678-8b2e-f18e0f6b7fc1.png b/pics/3290673d-edab-4678-8b2e-f18e0f6b7fc1.png new file mode 100644 index 00000000..8322e80a Binary files /dev/null and b/pics/3290673d-edab-4678-8b2e-f18e0f6b7fc1.png differ diff --git a/pics/3294ff06-f942-425e-aecc-ca04e45566d4.png b/pics/3294ff06-f942-425e-aecc-ca04e45566d4.png deleted file mode 100644 index 52d4305b..00000000 Binary files a/pics/3294ff06-f942-425e-aecc-ca04e45566d4.png and /dev/null differ diff --git a/pics/33a4e822-2dd0-481e-ac89-7f6161034402.jpg b/pics/33a4e822-2dd0-481e-ac89-7f6161034402.jpg deleted file mode 100644 index 86deea69..00000000 Binary files a/pics/33a4e822-2dd0-481e-ac89-7f6161034402.jpg and /dev/null differ diff --git a/pics/33ac2b23-cb85-4e99-bc41-b7b7199fad1c.png b/pics/33ac2b23-cb85-4e99-bc41-b7b7199fad1c.png new file mode 100644 index 00000000..6e8383f3 Binary files /dev/null and b/pics/33ac2b23-cb85-4e99-bc41-b7b7199fad1c.png differ diff --git a/pics/3402d1c0-7020-4249-9a7f-12ea2ea6adf7.jpg b/pics/3402d1c0-7020-4249-9a7f-12ea2ea6adf7.jpg deleted file mode 100644 index 8a1c0cc7..00000000 Binary files a/pics/3402d1c0-7020-4249-9a7f-12ea2ea6adf7.jpg and /dev/null differ diff --git a/pics/341c632a-1fc1-4068-9b9f-bf7ef68ebb4c.jpg b/pics/341c632a-1fc1-4068-9b9f-bf7ef68ebb4c.jpg deleted file mode 100644 index 8dbd591e..00000000 Binary files a/pics/341c632a-1fc1-4068-9b9f-bf7ef68ebb4c.jpg and /dev/null differ diff --git a/pics/346244ff-98c1-4f12-9a87-d0832e8c04cf.jpg b/pics/346244ff-98c1-4f12-9a87-d0832e8c04cf.jpg deleted file mode 100644 index a25da37c..00000000 Binary files a/pics/346244ff-98c1-4f12-9a87-d0832e8c04cf.jpg and /dev/null differ diff --git a/pics/348bc2db-582e-4aca-9f88-38c40e9a0e69.png b/pics/348bc2db-582e-4aca-9f88-38c40e9a0e69.png new file mode 100644 index 00000000..cb7e3681 Binary files /dev/null and b/pics/348bc2db-582e-4aca-9f88-38c40e9a0e69.png differ diff --git a/pics/350048d6-20f5-4ca9-8452-3957a09ef3af.png b/pics/350048d6-20f5-4ca9-8452-3957a09ef3af.png deleted file mode 100644 index df34cbdb..00000000 Binary files a/pics/350048d6-20f5-4ca9-8452-3957a09ef3af.png and /dev/null differ diff --git a/pics/352dd00d-d1bb-4134-845d-16a75bcb0e02.jpg b/pics/352dd00d-d1bb-4134-845d-16a75bcb0e02.jpg deleted file mode 100644 index 061099c6..00000000 Binary files a/pics/352dd00d-d1bb-4134-845d-16a75bcb0e02.jpg and /dev/null differ diff --git a/pics/35b0caf8-6f34-49db-93ed-d505e9eb3d99.png b/pics/35b0caf8-6f34-49db-93ed-d505e9eb3d99.png deleted file mode 100644 index 71505568..00000000 Binary files a/pics/35b0caf8-6f34-49db-93ed-d505e9eb3d99.png and /dev/null differ diff --git a/pics/365e5a18-cf63-4b80-bb12-da6b650653f7.jpg b/pics/365e5a18-cf63-4b80-bb12-da6b650653f7.jpg deleted file mode 100644 index 76afb8a4..00000000 Binary files a/pics/365e5a18-cf63-4b80-bb12-da6b650653f7.jpg and /dev/null differ diff --git a/pics/37a72755-4890-4b42-9eab-b0084e0c54d9.png b/pics/37a72755-4890-4b42-9eab-b0084e0c54d9.png new file mode 100644 index 00000000..c74c59ef Binary files /dev/null and b/pics/37a72755-4890-4b42-9eab-b0084e0c54d9.png differ diff --git a/pics/37b74a34-251c-45f8-88a4-614ec953f7e9.png b/pics/37b74a34-251c-45f8-88a4-614ec953f7e9.png deleted file mode 100644 index 4bac48ab..00000000 Binary files a/pics/37b74a34-251c-45f8-88a4-614ec953f7e9.png and /dev/null differ diff --git a/pics/38b894a7-525e-4204-80de-ecc1acc52c46.jpg b/pics/38b894a7-525e-4204-80de-ecc1acc52c46.jpg deleted file mode 100644 index 91b39053..00000000 Binary files a/pics/38b894a7-525e-4204-80de-ecc1acc52c46.jpg and /dev/null differ diff --git a/pics/390c913b-5f31-444f-bbdb-2b88b688e7ce.jpg b/pics/390c913b-5f31-444f-bbdb-2b88b688e7ce.jpg new file mode 100644 index 00000000..78eb732b Binary files /dev/null and b/pics/390c913b-5f31-444f-bbdb-2b88b688e7ce.jpg differ diff --git a/pics/392fb173-9713-433c-b37b-ea63ba76eae4.png b/pics/392fb173-9713-433c-b37b-ea63ba76eae4.png deleted file mode 100644 index 6f3bbdbb..00000000 Binary files a/pics/392fb173-9713-433c-b37b-ea63ba76eae4.png and /dev/null differ diff --git a/pics/3939369b-3a4a-48a0-b9eb-3efae26dd400.png b/pics/3939369b-3a4a-48a0-b9eb-3efae26dd400.png new file mode 100644 index 00000000..c2cf9d1b Binary files /dev/null and b/pics/3939369b-3a4a-48a0-b9eb-3efae26dd400.png differ diff --git a/pics/39a27cca-c9af-482a-8a87-5522557a309e.jpg b/pics/39a27cca-c9af-482a-8a87-5522557a309e.jpg deleted file mode 100644 index 6e9ac942..00000000 Binary files a/pics/39a27cca-c9af-482a-8a87-5522557a309e.jpg and /dev/null differ diff --git a/pics/3a676c54-b559-4466-9b21-eb10f1e25879.jpg b/pics/3a676c54-b559-4466-9b21-eb10f1e25879.jpg deleted file mode 100644 index 9c7b10d5..00000000 Binary files a/pics/3a676c54-b559-4466-9b21-eb10f1e25879.jpg and /dev/null differ diff --git a/pics/3c99f603-c471-49df-86df-0a0c750f1948.png b/pics/3c99f603-c471-49df-86df-0a0c750f1948.png deleted file mode 100644 index 3a911bef..00000000 Binary files a/pics/3c99f603-c471-49df-86df-0a0c750f1948.png and /dev/null differ diff --git a/pics/3dc454fb-efd4-4eb8-afde-785b2182caeb.jpg b/pics/3dc454fb-efd4-4eb8-afde-785b2182caeb.jpg deleted file mode 100644 index 36df547d..00000000 Binary files a/pics/3dc454fb-efd4-4eb8-afde-785b2182caeb.jpg and /dev/null differ diff --git a/pics/3e2200b3-1c18-4853-ae42-7788e8e1f939.png b/pics/3e2200b3-1c18-4853-ae42-7788e8e1f939.png deleted file mode 100644 index 97b0d441..00000000 Binary files a/pics/3e2200b3-1c18-4853-ae42-7788e8e1f939.png and /dev/null differ diff --git a/pics/3e87de44-1c69-4365-8139-b22e8d4be347.png b/pics/3e87de44-1c69-4365-8139-b22e8d4be347.png deleted file mode 100644 index 47753730..00000000 Binary files a/pics/3e87de44-1c69-4365-8139-b22e8d4be347.png and /dev/null differ diff --git a/pics/3efca49f-eecf-41fc-83aa-6a4a95e025ea.png b/pics/3efca49f-eecf-41fc-83aa-6a4a95e025ea.png deleted file mode 100644 index 94f728b5..00000000 Binary files a/pics/3efca49f-eecf-41fc-83aa-6a4a95e025ea.png and /dev/null differ diff --git a/pics/3f8d8c9d-a9a9-4d7a-813c-2de05ee5a97e.jpg b/pics/3f8d8c9d-a9a9-4d7a-813c-2de05ee5a97e.jpg deleted file mode 100644 index af314b78..00000000 Binary files a/pics/3f8d8c9d-a9a9-4d7a-813c-2de05ee5a97e.jpg and /dev/null differ diff --git a/pics/40639782-5df2-4e96-a4f3-f9dd664d0ca1.jpg b/pics/40639782-5df2-4e96-a4f3-f9dd664d0ca1.jpg deleted file mode 100644 index c6a7115c..00000000 Binary files a/pics/40639782-5df2-4e96-a4f3-f9dd664d0ca1.jpg and /dev/null differ diff --git a/pics/40c3f8e5-3a20-45b6-a60c-77b9b952e104.jpg b/pics/40c3f8e5-3a20-45b6-a60c-77b9b952e104.jpg deleted file mode 100644 index dd57e354..00000000 Binary files a/pics/40c3f8e5-3a20-45b6-a60c-77b9b952e104.jpg and /dev/null differ diff --git a/pics/40d96c0d-156f-4eee-a183-2d597344f1cd.png b/pics/40d96c0d-156f-4eee-a183-2d597344f1cd.png deleted file mode 100644 index 15f3819b..00000000 Binary files a/pics/40d96c0d-156f-4eee-a183-2d597344f1cd.png and /dev/null differ diff --git a/pics/40fe0669-6ac2-46f1-8666-a03785ad7412.png b/pics/40fe0669-6ac2-46f1-8666-a03785ad7412.png deleted file mode 100644 index aa3a94d6..00000000 Binary files a/pics/40fe0669-6ac2-46f1-8666-a03785ad7412.png and /dev/null differ diff --git a/pics/4102b7d0-39b9-48d8-82ae-ac4addb7ebfb.jpg b/pics/4102b7d0-39b9-48d8-82ae-ac4addb7ebfb.jpg deleted file mode 100644 index a319bbb8..00000000 Binary files a/pics/4102b7d0-39b9-48d8-82ae-ac4addb7ebfb.jpg and /dev/null differ diff --git a/pics/41392d76-dd1d-4712-85d9-e8bb46b04a2d.png b/pics/41392d76-dd1d-4712-85d9-e8bb46b04a2d.png new file mode 100644 index 00000000..d2fd2c63 Binary files /dev/null and b/pics/41392d76-dd1d-4712-85d9-e8bb46b04a2d.png differ diff --git a/pics/4146e14b-56b9-433c-8e3d-74b1b325399c.jpg b/pics/4146e14b-56b9-433c-8e3d-74b1b325399c.jpg deleted file mode 100644 index c683acde..00000000 Binary files a/pics/4146e14b-56b9-433c-8e3d-74b1b325399c.jpg and /dev/null differ diff --git a/pics/41a4cb30-f393-4b3b-abe4-9941ccf8fa1f.jpg b/pics/41a4cb30-f393-4b3b-abe4-9941ccf8fa1f.jpg deleted file mode 100644 index e6880589..00000000 Binary files a/pics/41a4cb30-f393-4b3b-abe4-9941ccf8fa1f.jpg and /dev/null differ diff --git a/pics/426df589-6f97-4622-b74d-4a81fcb1da8e.png b/pics/426df589-6f97-4622-b74d-4a81fcb1da8e.png new file mode 100644 index 00000000..98327bba Binary files /dev/null and b/pics/426df589-6f97-4622-b74d-4a81fcb1da8e.png differ diff --git a/pics/42e17a80-b9fc-42a2-9ba8-68364fae3710.jpg b/pics/42e17a80-b9fc-42a2-9ba8-68364fae3710.jpg deleted file mode 100644 index 34c83548..00000000 Binary files a/pics/42e17a80-b9fc-42a2-9ba8-68364fae3710.jpg and /dev/null differ diff --git a/pics/439deca7-fed0-4c89-87e5-7088d10f1fdb.jpg b/pics/439deca7-fed0-4c89-87e5-7088d10f1fdb.jpg deleted file mode 100644 index 5a0e13e1..00000000 Binary files a/pics/439deca7-fed0-4c89-87e5-7088d10f1fdb.jpg and /dev/null differ diff --git a/pics/43f2cafa-3568-4a89-a895-4725666b94a6.png b/pics/43f2cafa-3568-4a89-a895-4725666b94a6.png new file mode 100644 index 00000000..010278a2 Binary files /dev/null and b/pics/43f2cafa-3568-4a89-a895-4725666b94a6.png differ diff --git a/pics/4440ad24-625b-489a-96c1-e5ab1b06a30f.png b/pics/4440ad24-625b-489a-96c1-e5ab1b06a30f.png deleted file mode 100644 index fb68a74b..00000000 Binary files a/pics/4440ad24-625b-489a-96c1-e5ab1b06a30f.png and /dev/null differ diff --git a/pics/44e1d90e-3fe6-4dd6-8dce-6daab12e7663.jpg b/pics/44e1d90e-3fe6-4dd6-8dce-6daab12e7663.jpg deleted file mode 100644 index b20afc33..00000000 Binary files a/pics/44e1d90e-3fe6-4dd6-8dce-6daab12e7663.jpg and /dev/null differ diff --git a/pics/450px-HTTP_persistent_connection.svg.png b/pics/450px-HTTP_persistent_connection.svg.png new file mode 100644 index 00000000..e4fb8ba2 Binary files /dev/null and b/pics/450px-HTTP_persistent_connection.svg.png differ diff --git a/pics/4583e24f-424b-4d50-8a14-2c38a1827d4a.png b/pics/4583e24f-424b-4d50-8a14-2c38a1827d4a.png new file mode 100644 index 00000000..be98ce69 Binary files /dev/null and b/pics/4583e24f-424b-4d50-8a14-2c38a1827d4a.png differ diff --git a/pics/45c86855-9b18-4cf4-a9a7-f8b6eb78d133.png b/pics/45c86855-9b18-4cf4-a9a7-f8b6eb78d133.png deleted file mode 100644 index 9a3ff54c..00000000 Binary files a/pics/45c86855-9b18-4cf4-a9a7-f8b6eb78d133.png and /dev/null differ diff --git a/pics/45e0e0bf-386d-4280-a341-a0b9496c7674.png b/pics/45e0e0bf-386d-4280-a341-a0b9496c7674.png new file mode 100644 index 00000000..32fb94aa Binary files /dev/null and b/pics/45e0e0bf-386d-4280-a341-a0b9496c7674.png differ diff --git a/pics/4885d0bc-1441-460f-bd75-a2088aa7f2d4.png b/pics/4885d0bc-1441-460f-bd75-a2088aa7f2d4.png new file mode 100644 index 00000000..2f8dd24f Binary files /dev/null and b/pics/4885d0bc-1441-460f-bd75-a2088aa7f2d4.png differ diff --git a/pics/488b2127-9ea9-48f3-a3b6-800f1684be12.png b/pics/488b2127-9ea9-48f3-a3b6-800f1684be12.png deleted file mode 100644 index e17e411d..00000000 Binary files a/pics/488b2127-9ea9-48f3-a3b6-800f1684be12.png and /dev/null differ diff --git a/pics/4995b547-5620-45af-89d7-10f35c9621a1.jpg b/pics/4995b547-5620-45af-89d7-10f35c9621a1.jpg deleted file mode 100644 index 180f8031..00000000 Binary files a/pics/4995b547-5620-45af-89d7-10f35c9621a1.jpg and /dev/null differ diff --git a/pics/4bb7ed45-ec14-4d31-9da4-94024d9d3b05.png b/pics/4bb7ed45-ec14-4d31-9da4-94024d9d3b05.png new file mode 100644 index 00000000..c5912612 Binary files /dev/null and b/pics/4bb7ed45-ec14-4d31-9da4-94024d9d3b05.png differ diff --git a/pics/4d741402-344d-4b7c-be01-e57184bcad0e.png b/pics/4d741402-344d-4b7c-be01-e57184bcad0e.png new file mode 100644 index 00000000..a4a5e7b0 Binary files /dev/null and b/pics/4d741402-344d-4b7c-be01-e57184bcad0e.png differ diff --git a/pics/4e3faf22-fa80-445e-a57d-594c37bb76e7.png b/pics/4e3faf22-fa80-445e-a57d-594c37bb76e7.png deleted file mode 100644 index 3e188a9f..00000000 Binary files a/pics/4e3faf22-fa80-445e-a57d-594c37bb76e7.png and /dev/null differ diff --git a/pics/4f48e806-f90b-4c09-a55f-ac0cd641c047.png b/pics/4f48e806-f90b-4c09-a55f-ac0cd641c047.png new file mode 100644 index 00000000..649d16cd Binary files /dev/null and b/pics/4f48e806-f90b-4c09-a55f-ac0cd641c047.png differ diff --git a/pics/4f67611d-492f-4958-9fa0-4948010e345f.jpg b/pics/4f67611d-492f-4958-9fa0-4948010e345f.jpg deleted file mode 100644 index ed6a317a..00000000 Binary files a/pics/4f67611d-492f-4958-9fa0-4948010e345f.jpg and /dev/null differ diff --git a/pics/4f67aa74-5bf5-4ea4-9a6e-2e07d8f5fa86.png b/pics/4f67aa74-5bf5-4ea4-9a6e-2e07d8f5fa86.png deleted file mode 100644 index 354e6fc3..00000000 Binary files a/pics/4f67aa74-5bf5-4ea4-9a6e-2e07d8f5fa86.png and /dev/null differ diff --git a/pics/4fc032e0-ac6f-4b42-9182-ee104a25e7a1.png b/pics/4fc032e0-ac6f-4b42-9182-ee104a25e7a1.png new file mode 100644 index 00000000..4c61977c Binary files /dev/null and b/pics/4fc032e0-ac6f-4b42-9182-ee104a25e7a1.png differ diff --git a/pics/4ff7eacd-0081-452e-9686-60a81e11bf73.jpg b/pics/4ff7eacd-0081-452e-9686-60a81e11bf73.jpg deleted file mode 100644 index 1a1a7871..00000000 Binary files a/pics/4ff7eacd-0081-452e-9686-60a81e11bf73.jpg and /dev/null differ diff --git a/pics/5144a411-0e46-4a84-a179-c9ad3240418f.png b/pics/5144a411-0e46-4a84-a179-c9ad3240418f.png deleted file mode 100644 index a0a9b6ce..00000000 Binary files a/pics/5144a411-0e46-4a84-a179-c9ad3240418f.png and /dev/null differ diff --git a/pics/51e2ed95-65b8-4ae9-8af3-65602d452a25.jpg b/pics/51e2ed95-65b8-4ae9-8af3-65602d452a25.jpg new file mode 100644 index 00000000..595cdc58 Binary files /dev/null and b/pics/51e2ed95-65b8-4ae9-8af3-65602d452a25.jpg differ diff --git a/pics/51fb761d-8ce0-4472-92ff-2f227ac7888a.png b/pics/51fb761d-8ce0-4472-92ff-2f227ac7888a.png new file mode 100644 index 00000000..d49b1727 Binary files /dev/null and b/pics/51fb761d-8ce0-4472-92ff-2f227ac7888a.png differ diff --git a/pics/521969c9-71f6-44a5-9c78-118530e5c135.png b/pics/521969c9-71f6-44a5-9c78-118530e5c135.png deleted file mode 100644 index 7ab5ab5c..00000000 Binary files a/pics/521969c9-71f6-44a5-9c78-118530e5c135.png and /dev/null differ diff --git a/pics/524a237c-ffd7-426f-99c2-929a6bf4c847.jpg b/pics/524a237c-ffd7-426f-99c2-929a6bf4c847.jpg deleted file mode 100644 index 9f0aa101..00000000 Binary files a/pics/524a237c-ffd7-426f-99c2-929a6bf4c847.jpg and /dev/null differ diff --git a/pics/52726d32-e97a-49dd-8419-04d30a9f9050.png b/pics/52726d32-e97a-49dd-8419-04d30a9f9050.png deleted file mode 100644 index cf474fbc..00000000 Binary files a/pics/52726d32-e97a-49dd-8419-04d30a9f9050.png and /dev/null differ diff --git a/pics/52bb87e0-983e-4dd0-8e71-46dd2f72c97c.jpg b/pics/52bb87e0-983e-4dd0-8e71-46dd2f72c97c.jpg deleted file mode 100644 index 6436c8c9..00000000 Binary files a/pics/52bb87e0-983e-4dd0-8e71-46dd2f72c97c.jpg and /dev/null differ diff --git a/pics/52e1af6f-3a7a-4bee-aa8f-fcb5dacebe40.jpg b/pics/52e1af6f-3a7a-4bee-aa8f-fcb5dacebe40.jpg new file mode 100644 index 00000000..809abead Binary files /dev/null and b/pics/52e1af6f-3a7a-4bee-aa8f-fcb5dacebe40.jpg differ diff --git a/pics/540133af-aaaf-4208-8f7f-33cb89ac9621.png b/pics/540133af-aaaf-4208-8f7f-33cb89ac9621.png deleted file mode 100644 index 48ad4cae..00000000 Binary files a/pics/540133af-aaaf-4208-8f7f-33cb89ac9621.png and /dev/null differ diff --git a/pics/543d47a1-f0dd-414f-b23c-0c142c814854.png b/pics/543d47a1-f0dd-414f-b23c-0c142c814854.png deleted file mode 100644 index 5a3bbd02..00000000 Binary files a/pics/543d47a1-f0dd-414f-b23c-0c142c814854.png and /dev/null differ diff --git a/pics/54cb3f21-485b-4159-8bf5-dcde1c4d4c36.png b/pics/54cb3f21-485b-4159-8bf5-dcde1c4d4c36.png new file mode 100644 index 00000000..3a69d2ce Binary files /dev/null and b/pics/54cb3f21-485b-4159-8bf5-dcde1c4d4c36.png differ diff --git a/pics/5510045a-8f32-487f-a756-463e51a6dab0.png b/pics/5510045a-8f32-487f-a756-463e51a6dab0.png deleted file mode 100644 index 597ba533..00000000 Binary files a/pics/5510045a-8f32-487f-a756-463e51a6dab0.png and /dev/null differ diff --git a/pics/55dc4e84-573d-4c13-a765-52ed1dd251f9.png b/pics/55dc4e84-573d-4c13-a765-52ed1dd251f9.png new file mode 100644 index 00000000..525b323a Binary files /dev/null and b/pics/55dc4e84-573d-4c13-a765-52ed1dd251f9.png differ diff --git a/pics/58633775-8584-4a01-ad3f-eee4d9a466e1.jpg b/pics/58633775-8584-4a01-ad3f-eee4d9a466e1.jpg deleted file mode 100644 index 15f2d82c..00000000 Binary files a/pics/58633775-8584-4a01-ad3f-eee4d9a466e1.jpg and /dev/null differ diff --git a/pics/58b9926c-b56c-42f7-82e3-86aa0c164d0a.jpg b/pics/58b9926c-b56c-42f7-82e3-86aa0c164d0a.jpg deleted file mode 100644 index 0b07fd8d..00000000 Binary files a/pics/58b9926c-b56c-42f7-82e3-86aa0c164d0a.jpg and /dev/null differ diff --git a/pics/5930aeb8-847d-4e9f-a168-9334d7dec744.png b/pics/5930aeb8-847d-4e9f-a168-9334d7dec744.png new file mode 100644 index 00000000..6c9a572e Binary files /dev/null and b/pics/5930aeb8-847d-4e9f-a168-9334d7dec744.png differ diff --git a/pics/5994928c-3d2d-45bd-abb1-adc4f5f4d775.jpg b/pics/5994928c-3d2d-45bd-abb1-adc4f5f4d775.jpg deleted file mode 100644 index 7d64c8b8..00000000 Binary files a/pics/5994928c-3d2d-45bd-abb1-adc4f5f4d775.jpg and /dev/null differ diff --git a/pics/5999e5de-7c16-4b52-b3aa-6dc7b58c7894.png b/pics/5999e5de-7c16-4b52-b3aa-6dc7b58c7894.png new file mode 100644 index 00000000..a61a7dc4 Binary files /dev/null and b/pics/5999e5de-7c16-4b52-b3aa-6dc7b58c7894.png differ diff --git a/pics/59aff6c1-8bc5-48e4-9e9c-082baeb2f274.jpg b/pics/59aff6c1-8bc5-48e4-9e9c-082baeb2f274.jpg deleted file mode 100644 index 6c600949..00000000 Binary files a/pics/59aff6c1-8bc5-48e4-9e9c-082baeb2f274.jpg and /dev/null differ diff --git a/pics/5aa82b89-f266-44da-887d-18f31f01d8ef.png b/pics/5aa82b89-f266-44da-887d-18f31f01d8ef.png new file mode 100644 index 00000000..68310502 Binary files /dev/null and b/pics/5aa82b89-f266-44da-887d-18f31f01d8ef.png differ diff --git a/pics/5acf7550-86c5-4c5b-b912-8ce70ef9c34e.png b/pics/5acf7550-86c5-4c5b-b912-8ce70ef9c34e.png new file mode 100644 index 00000000..e209482b Binary files /dev/null and b/pics/5acf7550-86c5-4c5b-b912-8ce70ef9c34e.png differ diff --git a/pics/5b832bde-d05e-42db-b648-42e274571ad9.jpg b/pics/5b832bde-d05e-42db-b648-42e274571ad9.jpg deleted file mode 100644 index 8a478f5b..00000000 Binary files a/pics/5b832bde-d05e-42db-b648-42e274571ad9.jpg and /dev/null differ diff --git a/pics/5bbb64ff-bb9a-473e-ab53-8a26a394f813.jpg b/pics/5bbb64ff-bb9a-473e-ab53-8a26a394f813.jpg deleted file mode 100644 index f5c7b17d..00000000 Binary files a/pics/5bbb64ff-bb9a-473e-ab53-8a26a394f813.jpg and /dev/null differ diff --git a/pics/5c0bb285-b917-446b-84a2-9810ee40d041.png b/pics/5c0bb285-b917-446b-84a2-9810ee40d041.png deleted file mode 100644 index a064cc93..00000000 Binary files a/pics/5c0bb285-b917-446b-84a2-9810ee40d041.png and /dev/null differ diff --git a/pics/5c0bb285-b917-446b-84a2-9810ee41521898714517.png b/pics/5c0bb285-b917-446b-84a2-9810ee41521898714517.png deleted file mode 100644 index a064cc93..00000000 Binary files a/pics/5c0bb285-b917-446b-84a2-9810ee41521898714517.png and /dev/null differ diff --git a/pics/5c558190-fccd-4b5e-98ed-1896653fc97f.jpg b/pics/5c558190-fccd-4b5e-98ed-1896653fc97f.jpg deleted file mode 100644 index 5788fc85..00000000 Binary files a/pics/5c558190-fccd-4b5e-98ed-1896653fc97f.jpg and /dev/null differ diff --git a/pics/5ce4bdad-5ba1-4f60-81c3-874659412a5c.jpg b/pics/5ce4bdad-5ba1-4f60-81c3-874659412a5c.jpg deleted file mode 100644 index 9d167cdf..00000000 Binary files a/pics/5ce4bdad-5ba1-4f60-81c3-874659412a5c.jpg and /dev/null differ diff --git a/pics/5d387d02-6f96-44d6-b5d0-4538349f868e.png b/pics/5d387d02-6f96-44d6-b5d0-4538349f868e.png deleted file mode 100644 index f37e74ef..00000000 Binary files a/pics/5d387d02-6f96-44d6-b5d0-4538349f868e.png and /dev/null differ diff --git a/pics/5d4a5181-65fb-4bf2-a9c6-899cab534b44.png b/pics/5d4a5181-65fb-4bf2-a9c6-899cab534b44.png new file mode 100644 index 00000000..110bea35 Binary files /dev/null and b/pics/5d4a5181-65fb-4bf2-a9c6-899cab534b44.png differ diff --git a/pics/5e0cef33-4087-4f21-a428-16d5fddda671.jpg b/pics/5e0cef33-4087-4f21-a428-16d5fddda671.jpg deleted file mode 100644 index 85d2d7b5..00000000 Binary files a/pics/5e0cef33-4087-4f21-a428-16d5fddda671.jpg and /dev/null differ diff --git a/pics/5e8493be-72cc-4a76-a68f-4852eacb2811.png b/pics/5e8493be-72cc-4a76-a68f-4852eacb2811.png deleted file mode 100644 index 5404e6d2..00000000 Binary files a/pics/5e8493be-72cc-4a76-a68f-4852eacb2811.png and /dev/null differ diff --git a/pics/5ef94f62-98ce-464d-a646-842d9c72c8b8.jpg b/pics/5ef94f62-98ce-464d-a646-842d9c72c8b8.jpg deleted file mode 100644 index 6d083c11..00000000 Binary files a/pics/5ef94f62-98ce-464d-a646-842d9c72c8b8.jpg and /dev/null differ diff --git a/pics/5f96e565-0693-47df-80f1-29e4271057b7.png b/pics/5f96e565-0693-47df-80f1-29e4271057b7.png new file mode 100644 index 00000000..4da1e286 Binary files /dev/null and b/pics/5f96e565-0693-47df-80f1-29e4271057b7.png differ diff --git a/pics/600px-Sharedmem.jpg b/pics/600px-Sharedmem.jpg new file mode 100644 index 00000000..76cb2a48 Binary files /dev/null and b/pics/600px-Sharedmem.jpg differ diff --git a/pics/61b4832d-71f3-413c-84b6-237e219b9fdc.png b/pics/61b4832d-71f3-413c-84b6-237e219b9fdc.png deleted file mode 100644 index 70be236c..00000000 Binary files a/pics/61b4832d-71f3-413c-84b6-237e219b9fdc.png and /dev/null differ diff --git a/pics/62077f5d-a06d-4129-9b43-78715b82cb03.png b/pics/62077f5d-a06d-4129-9b43-78715b82cb03.png deleted file mode 100644 index 58ae8ca7..00000000 Binary files a/pics/62077f5d-a06d-4129-9b43-78715b82cb03.png and /dev/null differ diff --git a/pics/62ebbb63-8fd7-4488-a866-76a9dc911662.png b/pics/62ebbb63-8fd7-4488-a866-76a9dc911662.png deleted file mode 100644 index 805c69d0..00000000 Binary files a/pics/62ebbb63-8fd7-4488-a866-76a9dc911662.png and /dev/null differ diff --git a/pics/643a2587-08ae-4d92-94fb-d9a1c448cd13.png b/pics/643a2587-08ae-4d92-94fb-d9a1c448cd13.png deleted file mode 100644 index 236088dd..00000000 Binary files a/pics/643a2587-08ae-4d92-94fb-d9a1c448cd13.png and /dev/null differ diff --git a/pics/6468a541-3a9a-4008-82b6-03a0fe941d2a.png b/pics/6468a541-3a9a-4008-82b6-03a0fe941d2a.png deleted file mode 100644 index bd027b48..00000000 Binary files a/pics/6468a541-3a9a-4008-82b6-03a0fe941d2a.png and /dev/null differ diff --git a/pics/64b70b59-5bae-4dd2-addd-7f687ea5b64b.png b/pics/64b70b59-5bae-4dd2-addd-7f687ea5b64b.png deleted file mode 100644 index f21a3bc5..00000000 Binary files a/pics/64b70b59-5bae-4dd2-addd-7f687ea5b64b.png and /dev/null differ diff --git a/pics/64b95403-d976-421a-8b45-bac89c0b5185.jpg b/pics/64b95403-d976-421a-8b45-bac89c0b5185.jpg deleted file mode 100644 index 5486d7c1..00000000 Binary files a/pics/64b95403-d976-421a-8b45-bac89c0b5185.jpg and /dev/null differ diff --git a/pics/66192382-558b-4b05-a35d-ac4a2b1a9811.jpg b/pics/66192382-558b-4b05-a35d-ac4a2b1a9811.jpg new file mode 100644 index 00000000..eec226c5 Binary files /dev/null and b/pics/66192382-558b-4b05-a35d-ac4a2b1a9811.jpg differ diff --git a/pics/664f8901-5dc7-4644-a072-dad88cc5133a.jpg b/pics/664f8901-5dc7-4644-a072-dad88cc5133a.jpg deleted file mode 100644 index 7854f1bb..00000000 Binary files a/pics/664f8901-5dc7-4644-a072-dad88cc5133a.jpg and /dev/null differ diff --git a/pics/688dacfe-1057-412f-b3a1-86abb5b0f914.png b/pics/688dacfe-1057-412f-b3a1-86abb5b0f914.png new file mode 100644 index 00000000..21fa725f Binary files /dev/null and b/pics/688dacfe-1057-412f-b3a1-86abb5b0f914.png differ diff --git a/pics/691e8da5-fa65-4ee0-a4a9-bd9adba1521899256460.jpg b/pics/691e8da5-fa65-4ee0-a4a9-bd9adba1521899256460.jpg deleted file mode 100644 index e65a4083..00000000 Binary files a/pics/691e8da5-fa65-4ee0-a4a9-bd9adba1521899256460.jpg and /dev/null differ diff --git a/pics/691e8da5-fa65-4ee0-a4a9-bd9adba945ff.jpg b/pics/691e8da5-fa65-4ee0-a4a9-bd9adba945ff.jpg deleted file mode 100644 index e65a4083..00000000 Binary files a/pics/691e8da5-fa65-4ee0-a4a9-bd9adba945ff.jpg and /dev/null differ diff --git a/pics/6943e2af-5a70-4004-8bee-b33d60f39da3.jpg b/pics/6943e2af-5a70-4004-8bee-b33d60f39da3.jpg deleted file mode 100644 index 6ab91a1a..00000000 Binary files a/pics/6943e2af-5a70-4004-8bee-b33d60f39da3.jpg and /dev/null differ diff --git a/pics/69f16984-a66f-4288-82e4-79b4aa43e835.jpg b/pics/69f16984-a66f-4288-82e4-79b4aa43e835.jpg new file mode 100644 index 00000000..03b7f3da Binary files /dev/null and b/pics/69f16984-a66f-4288-82e4-79b4aa43e835.jpg differ diff --git a/pics/6afa9796-af1a-4495-9f02-63349ab68a19.png b/pics/6afa9796-af1a-4495-9f02-63349ab68a19.png deleted file mode 100644 index ee2ee55e..00000000 Binary files a/pics/6afa9796-af1a-4495-9f02-63349ab68a19.png and /dev/null differ diff --git a/pics/6bc61bb8-3b1c-4dc8-ac25-cef925ace0eb.jpg b/pics/6bc61bb8-3b1c-4dc8-ac25-cef925ace0eb.jpg deleted file mode 100644 index 3ba4690b..00000000 Binary files a/pics/6bc61bb8-3b1c-4dc8-ac25-cef925ace0eb.jpg and /dev/null differ diff --git a/pics/6e11b122-95ce-4869-bf7d-3b0d7591707e.jpg b/pics/6e11b122-95ce-4869-bf7d-3b0d7591707e.jpg deleted file mode 100644 index 1ebfdbdb..00000000 Binary files a/pics/6e11b122-95ce-4869-bf7d-3b0d7591707e.jpg and /dev/null differ diff --git a/pics/6e2cb20a-8d2a-46fe-9ac7-68a2126b7bd5.jpg b/pics/6e2cb20a-8d2a-46fe-9ac7-68a2126b7bd5.jpg deleted file mode 100644 index b2ce1e5e..00000000 Binary files a/pics/6e2cb20a-8d2a-46fe-9ac7-68a2126b7bd5.jpg and /dev/null differ diff --git a/pics/6e874d3e-9999-4d14-b6c9-fc7776c7ce30.png b/pics/6e874d3e-9999-4d14-b6c9-fc7776c7ce30.png deleted file mode 100644 index 6946dc22..00000000 Binary files a/pics/6e874d3e-9999-4d14-b6c9-fc7776c7ce30.png and /dev/null differ diff --git a/pics/6e8f16d1-7dea-4331-aea0-3dd739db00a4.png b/pics/6e8f16d1-7dea-4331-aea0-3dd739db00a4.png deleted file mode 100644 index 2bc0a265..00000000 Binary files a/pics/6e8f16d1-7dea-4331-aea0-3dd739db00a4.png and /dev/null differ diff --git a/pics/6e9bd38c-0d23-4ce1-a1f1-8bc302165360.jpg b/pics/6e9bd38c-0d23-4ce1-a1f1-8bc302165360.jpg deleted file mode 100644 index c0e0130b..00000000 Binary files a/pics/6e9bd38c-0d23-4ce1-a1f1-8bc302165360.jpg and /dev/null differ diff --git a/pics/6f4af159-8b03-4246-8d0e-222db65bb83c.jpg b/pics/6f4af159-8b03-4246-8d0e-222db65bb83c.jpg deleted file mode 100644 index f0e0d700..00000000 Binary files a/pics/6f4af159-8b03-4246-8d0e-222db65bb83c.jpg and /dev/null differ diff --git a/pics/6f5ed46f-86d7-4852-a34f-c1cf1b6343a0.png b/pics/6f5ed46f-86d7-4852-a34f-c1cf1b6343a0.png deleted file mode 100644 index 7cf1c9cb..00000000 Binary files a/pics/6f5ed46f-86d7-4852-a34f-c1cf1b6343a0.png and /dev/null differ diff --git a/pics/6fea6dd4-9232-4df8-abdf-f1b528a18b17.jpg b/pics/6fea6dd4-9232-4df8-abdf-f1b528a18b17.jpg deleted file mode 100644 index 99c96910..00000000 Binary files a/pics/6fea6dd4-9232-4df8-abdf-f1b528a18b17.jpg and /dev/null differ diff --git a/pics/6fec7f56-a685-4232-b03e-c92a8dfba486.png b/pics/6fec7f56-a685-4232-b03e-c92a8dfba486.png deleted file mode 100644 index b627cea5..00000000 Binary files a/pics/6fec7f56-a685-4232-b03e-c92a8dfba486.png and /dev/null differ diff --git a/pics/7080a928-06ba-4e10-9792-b8dd190dc8e2.jpg b/pics/7080a928-06ba-4e10-9792-b8dd190dc8e2.jpg deleted file mode 100644 index fb06a9c4..00000000 Binary files a/pics/7080a928-06ba-4e10-9792-b8dd190dc8e2.jpg and /dev/null differ diff --git a/pics/70a09383-f432-4b0f-ba42-b5b30d104f0b.jpg b/pics/70a09383-f432-4b0f-ba42-b5b30d104f0b.jpg deleted file mode 100644 index 1057068a..00000000 Binary files a/pics/70a09383-f432-4b0f-ba42-b5b30d104f0b.jpg and /dev/null differ diff --git a/pics/70b66757-755c-4e17-a7b7-5ce808023643.png b/pics/70b66757-755c-4e17-a7b7-5ce808023643.png deleted file mode 100644 index 0b834d6e..00000000 Binary files a/pics/70b66757-755c-4e17-a7b7-5ce808023643.png and /dev/null differ diff --git a/pics/71363383-2d06-4c63-8b72-c01c2186707d.png b/pics/71363383-2d06-4c63-8b72-c01c2186707d.png new file mode 100644 index 00000000..621820c3 Binary files /dev/null and b/pics/71363383-2d06-4c63-8b72-c01c2186707d.png differ diff --git a/pics/72f0ff69-138d-4e54-b7ac-ebe025d978dc.png b/pics/72f0ff69-138d-4e54-b7ac-ebe025d978dc.png new file mode 100644 index 00000000..5299728f Binary files /dev/null and b/pics/72f0ff69-138d-4e54-b7ac-ebe025d978dc.png differ diff --git a/pics/72f9bc11-06a9-40b4-8939-14f72e5cb4c3.png b/pics/72f9bc11-06a9-40b4-8939-14f72e5cb4c3.png deleted file mode 100644 index 605cd164..00000000 Binary files a/pics/72f9bc11-06a9-40b4-8939-14f72e5cb4c3.png and /dev/null differ diff --git a/pics/73a3983d-dd18-4373-897e-64b706a7e370.jpg b/pics/73a3983d-dd18-4373-897e-64b706a7e370.jpg deleted file mode 100644 index 1fd0b8ef..00000000 Binary files a/pics/73a3983d-dd18-4373-897e-64b706a7e370.jpg and /dev/null differ diff --git a/pics/73ecb593-664e-490e-80e9-4319773113ef.png b/pics/73ecb593-664e-490e-80e9-4319773113ef.png deleted file mode 100644 index 6bda394f..00000000 Binary files a/pics/73ecb593-664e-490e-80e9-4319773113ef.png and /dev/null differ diff --git a/pics/750501be-6b8a-4cb5-a807-371a218ee612.png b/pics/750501be-6b8a-4cb5-a807-371a218ee612.png deleted file mode 100644 index 40aa7233..00000000 Binary files a/pics/750501be-6b8a-4cb5-a807-371a218ee612.png and /dev/null differ diff --git a/pics/760a5d63-d96d-4dd9-bf9a-c3d126b2f401.jpg b/pics/760a5d63-d96d-4dd9-bf9a-c3d126b2f401.jpg deleted file mode 100644 index 48eabcf6..00000000 Binary files a/pics/760a5d63-d96d-4dd9-bf9a-c3d126b2f401.jpg and /dev/null differ diff --git a/pics/77f81379-3987-4036-8d7c-93a4dcf7b05d.jpg b/pics/77f81379-3987-4036-8d7c-93a4dcf7b05d.jpg new file mode 100644 index 00000000..bc5ddb98 Binary files /dev/null and b/pics/77f81379-3987-4036-8d7c-93a4dcf7b05d.jpg differ diff --git a/pics/78534153-88d1-4f83-a6e0-59064dbdc43a.png b/pics/78534153-88d1-4f83-a6e0-59064dbdc43a.png deleted file mode 100644 index e42a0de4..00000000 Binary files a/pics/78534153-88d1-4f83-a6e0-59064dbdc43a.png and /dev/null differ diff --git a/pics/78570a06-0781-4f9d-9093-70a87111521898809910.jpg b/pics/78570a06-0781-4f9d-9093-70a87111521898809910.jpg deleted file mode 100644 index 2aa55f8f..00000000 Binary files a/pics/78570a06-0781-4f9d-9093-70a87111521898809910.jpg and /dev/null differ diff --git a/pics/78570a06-0781-4f9d-9093-70a8711785b5.jpg b/pics/78570a06-0781-4f9d-9093-70a8711785b5.jpg deleted file mode 100644 index 2aa55f8f..00000000 Binary files a/pics/78570a06-0781-4f9d-9093-70a8711785b5.jpg and /dev/null differ diff --git a/pics/785806ed-c46b-4dca-b756-cebe7bf8ac3a.jpg b/pics/785806ed-c46b-4dca-b756-cebe7bf8ac3a.jpg deleted file mode 100644 index a9469812..00000000 Binary files a/pics/785806ed-c46b-4dca-b756-cebe7bf8ac3a.jpg and /dev/null differ diff --git a/pics/78f2314e-2643-41df-8f3d-b7e28294094b.jpg b/pics/78f2314e-2643-41df-8f3d-b7e28294094b.jpg deleted file mode 100644 index 586f2ae4..00000000 Binary files a/pics/78f2314e-2643-41df-8f3d-b7e28294094b.jpg and /dev/null differ diff --git a/pics/78f65456-666b-4044-b4ee-f7692dbbc0d3.jpg b/pics/78f65456-666b-4044-b4ee-f7692dbbc0d3.jpg deleted file mode 100644 index 93439ff0..00000000 Binary files a/pics/78f65456-666b-4044-b4ee-f7692dbbc0d3.jpg and /dev/null differ diff --git a/pics/79b12431-6d9d-4a7d-985b-1b79bc5bf5fb.png b/pics/79b12431-6d9d-4a7d-985b-1b79bc5bf5fb.png new file mode 100644 index 00000000..d2a30004 Binary files /dev/null and b/pics/79b12431-6d9d-4a7d-985b-1b79bc5bf5fb.png differ diff --git a/pics/79f28233-f5cb-492a-9dc4-696cb714d434.png b/pics/79f28233-f5cb-492a-9dc4-696cb714d434.png deleted file mode 100644 index 34e4b4a1..00000000 Binary files a/pics/79f28233-f5cb-492a-9dc4-696cb714d434.png and /dev/null differ diff --git a/pics/7b038838-c75b-4538-ae84-6299386704e5.jpg b/pics/7b038838-c75b-4538-ae84-6299386704e5.jpg new file mode 100644 index 00000000..919a0e58 Binary files /dev/null and b/pics/7b038838-c75b-4538-ae84-6299386704e5.jpg differ diff --git a/pics/7b3efa99-d306-4982-8cfb-e7153c33aab4.png b/pics/7b3efa99-d306-4982-8cfb-e7153c33aab4.png new file mode 100644 index 00000000..21aafd0a Binary files /dev/null and b/pics/7b3efa99-d306-4982-8cfb-e7153c33aab4.png differ diff --git a/pics/7b68b142-9489-44f6-87b0-4cb5c6431e63.jpg b/pics/7b68b142-9489-44f6-87b0-4cb5c6431e63.jpg new file mode 100644 index 00000000..d4380524 Binary files /dev/null and b/pics/7b68b142-9489-44f6-87b0-4cb5c6431e63.jpg differ diff --git a/pics/7b877a2a-8fd1-40d8-a34c-c445827300b8.jpg b/pics/7b877a2a-8fd1-40d8-a34c-c445827300b8.jpg deleted file mode 100644 index 5e5d19cc..00000000 Binary files a/pics/7b877a2a-8fd1-40d8-a34c-c445827300b8.jpg and /dev/null differ diff --git a/pics/7b8f0d8e-a4fa-4c9d-b9a0-3e6a11cb3e33.jpg b/pics/7b8f0d8e-a4fa-4c9d-b9a0-3e6a11cb3e33.jpg deleted file mode 100644 index 522090c6..00000000 Binary files a/pics/7b8f0d8e-a4fa-4c9d-b9a0-3e6a11cb3e33.jpg and /dev/null differ diff --git a/pics/7c98e1b6-c446-4cde-8513-5c11b9f52aea.jpg b/pics/7c98e1b6-c446-4cde-8513-5c11b9f52aea.jpg new file mode 100644 index 00000000..7dcb6d1c Binary files /dev/null and b/pics/7c98e1b6-c446-4cde-8513-5c11b9f52aea.jpg differ diff --git a/pics/7ca30d08-f7fa-4e39-b386-93c70631a900.png b/pics/7ca30d08-f7fa-4e39-b386-93c70631a900.png deleted file mode 100644 index dd852349..00000000 Binary files a/pics/7ca30d08-f7fa-4e39-b386-93c70631a900.png and /dev/null differ diff --git a/pics/7cfcfdf7-63a7-4111-a677-2eca29fbcf24.png b/pics/7cfcfdf7-63a7-4111-a677-2eca29fbcf24.png deleted file mode 100644 index ae7577ba..00000000 Binary files a/pics/7cfcfdf7-63a7-4111-a677-2eca29fbcf24.png and /dev/null differ diff --git a/pics/7e0b2961-2ffe-4a24-bef1-1b07a78af268.jpg b/pics/7e0b2961-2ffe-4a24-bef1-1b07a78af268.jpg deleted file mode 100644 index fcc0e3e3..00000000 Binary files a/pics/7e0b2961-2ffe-4a24-bef1-1b07a78af268.jpg and /dev/null differ diff --git a/pics/7e9d0ef2-acd8-44c0-a76b-f0d2f5e76738.png b/pics/7e9d0ef2-acd8-44c0-a76b-f0d2f5e76738.png deleted file mode 100644 index 3e5b9448..00000000 Binary files a/pics/7e9d0ef2-acd8-44c0-a76b-f0d2f5e76738.png and /dev/null differ diff --git a/pics/7f38a583-2f2e-4738-97af-510e6fb403a7.png b/pics/7f38a583-2f2e-4738-97af-510e6fb403a7.png new file mode 100644 index 00000000..57e71b9c Binary files /dev/null and b/pics/7f38a583-2f2e-4738-97af-510e6fb403a7.png differ diff --git a/pics/7f82fd18-7f16-4125-ada6-bb6b795b4fda.png b/pics/7f82fd18-7f16-4125-ada6-bb6b795b4fda.png deleted file mode 100644 index 342fc5f0..00000000 Binary files a/pics/7f82fd18-7f16-4125-ada6-bb6b795b4fda.png and /dev/null differ diff --git a/pics/7f9a9342-1491-436d-bdd2-0fa94eb0e4f1.png b/pics/7f9a9342-1491-436d-bdd2-0fa94eb0e4f1.png deleted file mode 100644 index 0c7f97e9..00000000 Binary files a/pics/7f9a9342-1491-436d-bdd2-0fa94eb0e4f1.png and /dev/null differ diff --git a/pics/7fcb2fb0-2cd9-4396-bc2d-282becf963c3.jpg b/pics/7fcb2fb0-2cd9-4396-bc2d-282becf963c3.jpg deleted file mode 100644 index 8f1e02f0..00000000 Binary files a/pics/7fcb2fb0-2cd9-4396-bc2d-282becf963c3.jpg and /dev/null differ diff --git a/pics/8006a450-6c2f-498c-a928-c927f758b1d0.png b/pics/8006a450-6c2f-498c-a928-c927f758b1d0.png new file mode 100644 index 00000000..ac453eeb Binary files /dev/null and b/pics/8006a450-6c2f-498c-a928-c927f758b1d0.png differ diff --git a/pics/81375888-6be1-476f-9521-42eea3e3154f.jpg b/pics/81375888-6be1-476f-9521-42eea3e3154f.jpg deleted file mode 100644 index 86f9e488..00000000 Binary files a/pics/81375888-6be1-476f-9521-42eea3e3154f.jpg and /dev/null differ diff --git a/pics/8143787f-12eb-46ea-9bc3-c66d22d35285.jpg b/pics/8143787f-12eb-46ea-9bc3-c66d22d35285.jpg deleted file mode 100644 index dbcffd88..00000000 Binary files a/pics/8143787f-12eb-46ea-9bc3-c66d22d35285.jpg and /dev/null differ diff --git a/pics/81a75fed-5c1d-4e4c-af4a-4c38c2a48927.jpg b/pics/81a75fed-5c1d-4e4c-af4a-4c38c2a48927.jpg deleted file mode 100644 index 6acdc930..00000000 Binary files a/pics/81a75fed-5c1d-4e4c-af4a-4c38c2a48927.jpg and /dev/null differ diff --git a/pics/81eb9879-40f2-421a-87de-2b953cfe8c32.png b/pics/81eb9879-40f2-421a-87de-2b953cfe8c32.png deleted file mode 100644 index d47cadb5..00000000 Binary files a/pics/81eb9879-40f2-421a-87de-2b953cfe8c32.png and /dev/null differ diff --git a/pics/81fd1d6f-a3b2-4160-9a0a-1f7cb50ba440.jpg b/pics/81fd1d6f-a3b2-4160-9a0a-1f7cb50ba440.jpg deleted file mode 100644 index 0dfa5179..00000000 Binary files a/pics/81fd1d6f-a3b2-4160-9a0a-1f7cb50ba440.jpg and /dev/null differ diff --git a/pics/8229e8e7-a183-4d29-94e6-e8d8537c6ce5.png b/pics/8229e8e7-a183-4d29-94e6-e8d8537c6ce5.png new file mode 100644 index 00000000..3aaa6323 Binary files /dev/null and b/pics/8229e8e7-a183-4d29-94e6-e8d8537c6ce5.png differ diff --git a/pics/8320bad6-3f91-4a15-8e3d-68e8f39649b5.png b/pics/8320bad6-3f91-4a15-8e3d-68e8f39649b5.png deleted file mode 100644 index 00e82151..00000000 Binary files a/pics/8320bad6-3f91-4a15-8e3d-68e8f39649b5.png and /dev/null differ diff --git a/pics/83575315-20b5-44a6-bf58-94460a141ffa.jpg b/pics/83575315-20b5-44a6-bf58-94460a141ffa.jpg deleted file mode 100644 index 568338fb..00000000 Binary files a/pics/83575315-20b5-44a6-bf58-94460a141ffa.jpg and /dev/null differ diff --git a/pics/836a4eaf-4798-4e48-b52a-a3dab9435ace.png b/pics/836a4eaf-4798-4e48-b52a-a3dab9435ace.png new file mode 100644 index 00000000..be8ab737 Binary files /dev/null and b/pics/836a4eaf-4798-4e48-b52a-a3dab9435ace.png differ diff --git a/pics/8393f520-d824-44ea-a5f3-1c1a73d735fb.jpg b/pics/8393f520-d824-44ea-a5f3-1c1a73d735fb.jpg deleted file mode 100644 index 3a56d4d2..00000000 Binary files a/pics/8393f520-d824-44ea-a5f3-1c1a73d735fb.jpg and /dev/null differ diff --git a/pics/8433fbb2-c35c-45ef-831d-e3ca42aebd51.png b/pics/8433fbb2-c35c-45ef-831d-e3ca42aebd51.png new file mode 100644 index 00000000..5c0b076b Binary files /dev/null and b/pics/8433fbb2-c35c-45ef-831d-e3ca42aebd51.png differ diff --git a/pics/8459a13f-b0b8-4387-85a9-2525482bc25a.png b/pics/8459a13f-b0b8-4387-85a9-2525482bc25a.png deleted file mode 100644 index 3735f081..00000000 Binary files a/pics/8459a13f-b0b8-4387-85a9-2525482bc25a.png and /dev/null differ diff --git a/pics/85c05fb1-5546-4c50-9221-21f231cdc8c5.jpg b/pics/85c05fb1-5546-4c50-9221-21f231cdc8c5.jpg new file mode 100644 index 00000000..4b911193 Binary files /dev/null and b/pics/85c05fb1-5546-4c50-9221-21f231cdc8c5.jpg differ diff --git a/pics/8615d9f7-bd1d-4240-8bb4-02b941d54a6f.png b/pics/8615d9f7-bd1d-4240-8bb4-02b941d54a6f.png deleted file mode 100644 index 5e13c216..00000000 Binary files a/pics/8615d9f7-bd1d-4240-8bb4-02b941d54a6f.png and /dev/null differ diff --git a/pics/867abc3c-8403-43ef-8847-c1fea32996a5.png b/pics/867abc3c-8403-43ef-8847-c1fea32996a5.png deleted file mode 100644 index e4cf4731..00000000 Binary files a/pics/867abc3c-8403-43ef-8847-c1fea32996a5.png and /dev/null differ diff --git a/pics/8681db55-0873-434b-aa98-83d07e8392ae.jpg b/pics/8681db55-0873-434b-aa98-83d07e8392ae.jpg deleted file mode 100644 index 3e6414b1..00000000 Binary files a/pics/8681db55-0873-434b-aa98-83d07e8392ae.jpg and /dev/null differ diff --git a/pics/86b71296-0d1e-4a63-bcd9-54955b6b781b.jpg b/pics/86b71296-0d1e-4a63-bcd9-54955b6b781b.jpg deleted file mode 100644 index c319156d..00000000 Binary files a/pics/86b71296-0d1e-4a63-bcd9-54955b6b781b.jpg and /dev/null differ diff --git a/pics/8785dabd-1285-4bd0-b3aa-b05cc060a24a.jpg b/pics/8785dabd-1285-4bd0-b3aa-b05cc060a24a.jpg deleted file mode 100644 index 7c7efcce..00000000 Binary files a/pics/8785dabd-1285-4bd0-b3aa-b05cc060a24a.jpg and /dev/null differ diff --git a/pics/87ffaf7f-4aa5-4da0-af84-994de62fa440.jpg b/pics/87ffaf7f-4aa5-4da0-af84-994de62fa440.jpg deleted file mode 100644 index e4860023..00000000 Binary files a/pics/87ffaf7f-4aa5-4da0-af84-994de62fa440.jpg and /dev/null differ diff --git a/pics/88ae5997-50ab-4204-891f-88e212ba892e.png b/pics/88ae5997-50ab-4204-891f-88e212ba892e.png deleted file mode 100644 index d17ca4c6..00000000 Binary files a/pics/88ae5997-50ab-4204-891f-88e212ba892e.png and /dev/null differ diff --git a/pics/897a4f4e-2683-44e1-a26a-c0d0234dc576.jpg b/pics/897a4f4e-2683-44e1-a26a-c0d0234dc576.jpg deleted file mode 100644 index c6f0a0b6..00000000 Binary files a/pics/897a4f4e-2683-44e1-a26a-c0d0234dc576.jpg and /dev/null differ diff --git a/pics/8a116e69-3d57-4987-a215-0197dd044a14.png b/pics/8a116e69-3d57-4987-a215-0197dd044a14.png deleted file mode 100644 index 636459da..00000000 Binary files a/pics/8a116e69-3d57-4987-a215-0197dd044a14.png and /dev/null differ diff --git a/pics/8a4c6ad4-a816-47d1-b93f-7ca4f78ab67a.png b/pics/8a4c6ad4-a816-47d1-b93f-7ca4f78ab67a.png new file mode 100644 index 00000000..360a984e Binary files /dev/null and b/pics/8a4c6ad4-a816-47d1-b93f-7ca4f78ab67a.png differ diff --git a/pics/8ab40d6d-bd7c-47d3-afe8-6a8bc9f5d04c.jpg b/pics/8ab40d6d-bd7c-47d3-afe8-6a8bc9f5d04c.jpg deleted file mode 100644 index c4eb4d8d..00000000 Binary files a/pics/8ab40d6d-bd7c-47d3-afe8-6a8bc9f5d04c.jpg and /dev/null differ diff --git a/pics/8ae4550b-f0cb-4e4d-9e2b-c550538bf230.png b/pics/8ae4550b-f0cb-4e4d-9e2b-c550538bf230.png new file mode 100644 index 00000000..31ae5e0c Binary files /dev/null and b/pics/8ae4550b-f0cb-4e4d-9e2b-c550538bf230.png differ diff --git a/pics/8af348d0-4d72-4f76-b56c-0a440ed4673d.png b/pics/8af348d0-4d72-4f76-b56c-0a440ed4673d.png new file mode 100644 index 00000000..9d575722 Binary files /dev/null and b/pics/8af348d0-4d72-4f76-b56c-0a440ed4673d.png differ diff --git a/pics/8b15e36f-69b4-46b6-a07c-7234ac7c7927.jpg b/pics/8b15e36f-69b4-46b6-a07c-7234ac7c7927.jpg deleted file mode 100644 index 58d6f04d..00000000 Binary files a/pics/8b15e36f-69b4-46b6-a07c-7234ac7c7927.jpg and /dev/null differ diff --git a/pics/8b5bd2c8-8425-4a8b-89db-235c95800de9.jpg b/pics/8b5bd2c8-8425-4a8b-89db-235c95800de9.jpg deleted file mode 100644 index 0d5e32aa..00000000 Binary files a/pics/8b5bd2c8-8425-4a8b-89db-235c95800de9.jpg and /dev/null differ diff --git a/pics/8bc6fc2c-d198-4759-b06c-18d94d851e97.png b/pics/8bc6fc2c-d198-4759-b06c-18d94d851e97.png deleted file mode 100644 index 92f32485..00000000 Binary files a/pics/8bc6fc2c-d198-4759-b06c-18d94d851e97.png and /dev/null differ diff --git a/pics/8bc990c1-a2bb-4885-afd7-db0f1ea87451.png b/pics/8bc990c1-a2bb-4885-afd7-db0f1ea87451.png deleted file mode 100644 index f4cf444d..00000000 Binary files a/pics/8bc990c1-a2bb-4885-afd7-db0f1ea87451.png and /dev/null differ diff --git a/pics/8c139711-3500-4f71-8456-c1adaf429ad0.png b/pics/8c139711-3500-4f71-8456-c1adaf429ad0.png deleted file mode 100644 index 4bd01d9c..00000000 Binary files a/pics/8c139711-3500-4f71-8456-c1adaf429ad0.png and /dev/null differ diff --git a/pics/8cc671f0-7134-44b1-a7b5-6d24fe55e1c1.jpg b/pics/8cc671f0-7134-44b1-a7b5-6d24fe55e1c1.jpg new file mode 100644 index 00000000..5e6608b2 Binary files /dev/null and b/pics/8cc671f0-7134-44b1-a7b5-6d24fe55e1c1.jpg differ diff --git a/pics/8d13ee52-e881-41eb-ad70-678c411f2718.png b/pics/8d13ee52-e881-41eb-ad70-678c411f2718.png deleted file mode 100644 index 90bbe673..00000000 Binary files a/pics/8d13ee52-e881-41eb-ad70-678c411f2718.png and /dev/null differ diff --git a/pics/8d211911-0e62-4190-ab00-d8610adec4a0.jpg b/pics/8d211911-0e62-4190-ab00-d8610adec4a0.jpg deleted file mode 100644 index b6c65270..00000000 Binary files a/pics/8d211911-0e62-4190-ab00-d8610adec4a0.jpg and /dev/null differ diff --git a/pics/8d6af5ac-74eb-4e07-99aa-654b9f21f1d3.jpg b/pics/8d6af5ac-74eb-4e07-99aa-654b9f21f1d3.jpg deleted file mode 100644 index 65b9370e..00000000 Binary files a/pics/8d6af5ac-74eb-4e07-99aa-654b9f21f1d3.jpg and /dev/null differ diff --git a/pics/8dfb4cc9-26da-45e7-b820-4576fa1cbb0e.png b/pics/8dfb4cc9-26da-45e7-b820-4576fa1cbb0e.png new file mode 100644 index 00000000..7044dfbd Binary files /dev/null and b/pics/8dfb4cc9-26da-45e7-b820-4576fa1cbb0e.png differ diff --git a/pics/8e8ba824-7a9e-4934-a212-e6a41dcc1602.jpg b/pics/8e8ba824-7a9e-4934-a212-e6a41dcc1602.jpg deleted file mode 100644 index b405a3b8..00000000 Binary files a/pics/8e8ba824-7a9e-4934-a212-e6a41dcc1602.jpg and /dev/null differ diff --git a/pics/8ef22836-8800-4765-b4b8-ade80096b323.jpg b/pics/8ef22836-8800-4765-b4b8-ade80096b323.jpg deleted file mode 100644 index b4d8268f..00000000 Binary files a/pics/8ef22836-8800-4765-b4b8-ade80096b323.jpg and /dev/null differ diff --git a/pics/8f0cc500-5994-4c7a-91a9-62885d658662.png b/pics/8f0cc500-5994-4c7a-91a9-62885d658662.png new file mode 100644 index 00000000..034205a8 Binary files /dev/null and b/pics/8f0cc500-5994-4c7a-91a9-62885d658662.png differ diff --git a/pics/8f1e2db5-a59b-4633-8b61-6b8b9505b8ea.png b/pics/8f1e2db5-a59b-4633-8b61-6b8b9505b8ea.png deleted file mode 100644 index e7d64cb2..00000000 Binary files a/pics/8f1e2db5-a59b-4633-8b61-6b8b9505b8ea.png and /dev/null differ diff --git a/pics/8f3b9519-d705-48fe-87ad-2e4052fc81d2.png b/pics/8f3b9519-d705-48fe-87ad-2e4052fc81d2.png new file mode 100644 index 00000000..a8e36ee0 Binary files /dev/null and b/pics/8f3b9519-d705-48fe-87ad-2e4052fc81d2.png differ diff --git a/pics/8f6f9dc9-9ecd-47c8-b50e-2814f0219056.png b/pics/8f6f9dc9-9ecd-47c8-b50e-2814f0219056.png new file mode 100644 index 00000000..21b8a930 Binary files /dev/null and b/pics/8f6f9dc9-9ecd-47c8-b50e-2814f0219056.png differ diff --git a/pics/903093ec-acc8-4f9b-bf2c-b990b9a5390c.jpg b/pics/903093ec-acc8-4f9b-bf2c-b990b9a5390c.jpg deleted file mode 100644 index a031071b..00000000 Binary files a/pics/903093ec-acc8-4f9b-bf2c-b990b9a5390c.jpg and /dev/null differ diff --git a/pics/910f613f-514f-4534-87dd-9b4699d59d31.png b/pics/910f613f-514f-4534-87dd-9b4699d59d31.png new file mode 100644 index 00000000..28362d47 Binary files /dev/null and b/pics/910f613f-514f-4534-87dd-9b4699d59d31.png differ diff --git a/pics/9110c1a0-8a54-4145-a814-2477d0128114.png b/pics/9110c1a0-8a54-4145-a814-2477d0128114.png new file mode 100644 index 00000000..68aa9f98 Binary files /dev/null and b/pics/9110c1a0-8a54-4145-a814-2477d0128114.png differ diff --git a/pics/912174d8-0786-4222-b7ef-a611d36e5db9.jpg b/pics/912174d8-0786-4222-b7ef-a611d36e5db9.jpg deleted file mode 100644 index 5f9cb884..00000000 Binary files a/pics/912174d8-0786-4222-b7ef-a611d36e5db9.jpg and /dev/null differ diff --git a/pics/9192dc0a-a7cd-4030-8df6-e388600644cf.jpg b/pics/9192dc0a-a7cd-4030-8df6-e388600644cf.jpg deleted file mode 100644 index 1917c941..00000000 Binary files a/pics/9192dc0a-a7cd-4030-8df6-e388600644cf.jpg and /dev/null differ diff --git a/pics/9208563b-014d-4745-aa1c-492c9f7f7a7f.jpg b/pics/9208563b-014d-4745-aa1c-492c9f7f7a7f.jpg deleted file mode 100644 index dadacb8a..00000000 Binary files a/pics/9208563b-014d-4745-aa1c-492c9f7f7a7f.jpg and /dev/null differ diff --git a/pics/923896c1-937e-4a38-b8a6-cec3040b4e2a.jpg b/pics/923896c1-937e-4a38-b8a6-cec3040b4e2a.jpg deleted file mode 100644 index ee440e1d..00000000 Binary files a/pics/923896c1-937e-4a38-b8a6-cec3040b4e2a.jpg and /dev/null differ diff --git a/pics/92ad9bae-7d02-43ba-8115-a9d6f530ca28.png b/pics/92ad9bae-7d02-43ba-8115-a9d6f530ca28.png new file mode 100644 index 00000000..7b85c49a Binary files /dev/null and b/pics/92ad9bae-7d02-43ba-8115-a9d6f530ca28.png differ diff --git a/pics/931e112e-97d3-4a47-ac64-a86d70844e58.png b/pics/931e112e-97d3-4a47-ac64-a86d70844e58.png deleted file mode 100644 index b6be65ec..00000000 Binary files a/pics/931e112e-97d3-4a47-ac64-a86d70844e58.png and /dev/null differ diff --git a/pics/93a28704-6401-4671-9758-051fadfbeb47.jpg b/pics/93a28704-6401-4671-9758-051fadfbeb47.jpg deleted file mode 100644 index 15f5f7cc..00000000 Binary files a/pics/93a28704-6401-4671-9758-051fadfbeb47.jpg and /dev/null differ diff --git a/pics/93cbce0c-c37d-429c-815b-861976a46bd8.png b/pics/93cbce0c-c37d-429c-815b-861976a46bd8.png deleted file mode 100644 index fca4da5d..00000000 Binary files a/pics/93cbce0c-c37d-429c-815b-861976a46bd8.png and /dev/null differ diff --git a/pics/94589319-975f-490b-8bae-90b3a4953559.png b/pics/94589319-975f-490b-8bae-90b3a4953559.png new file mode 100644 index 00000000..fab53e3d Binary files /dev/null and b/pics/94589319-975f-490b-8bae-90b3a4953559.png differ diff --git a/pics/955af054-8872-4569-82e7-2e10b66bc38e.png b/pics/955af054-8872-4569-82e7-2e10b66bc38e.png new file mode 100644 index 00000000..502af347 Binary files /dev/null and b/pics/955af054-8872-4569-82e7-2e10b66bc38e.png differ diff --git a/pics/95f4559c-3d2a-4176-b365-4fbc46c76cf1.png b/pics/95f4559c-3d2a-4176-b365-4fbc46c76cf1.png deleted file mode 100644 index 0085f33e..00000000 Binary files a/pics/95f4559c-3d2a-4176-b365-4fbc46c76cf1.png and /dev/null differ diff --git a/pics/9653b0c6-4232-4299-9f5c-79a616abafb8.png b/pics/9653b0c6-4232-4299-9f5c-79a616abafb8.png deleted file mode 100644 index 0a04997f..00000000 Binary files a/pics/9653b0c6-4232-4299-9f5c-79a616abafb8.png and /dev/null differ diff --git a/pics/967b2f5a-6ade-4ceb-bb41-493483fd3dff.png b/pics/967b2f5a-6ade-4ceb-bb41-493483fd3dff.png deleted file mode 100644 index 73a9046a..00000000 Binary files a/pics/967b2f5a-6ade-4ceb-bb41-493483fd3dff.png and /dev/null differ diff --git a/pics/98fb8a54-e916-440e-ab4f-73b2955838c9.jpg b/pics/98fb8a54-e916-440e-ab4f-73b2955838c9.jpg deleted file mode 100644 index 4919ca2c..00000000 Binary files a/pics/98fb8a54-e916-440e-ab4f-73b2955838c9.jpg and /dev/null differ diff --git a/pics/992faced-afcf-414d-b801-9c16d6570fec.jpg b/pics/992faced-afcf-414d-b801-9c16d6570fec.jpg new file mode 100644 index 00000000..1a363f1e Binary files /dev/null and b/pics/992faced-afcf-414d-b801-9c16d6570fec.jpg differ diff --git a/pics/9a30b932-f69f-40a1-9564-a1354ff8cf29.jpg b/pics/9a30b932-f69f-40a1-9564-a1354ff8cf29.jpg deleted file mode 100644 index 573a533f..00000000 Binary files a/pics/9a30b932-f69f-40a1-9564-a1354ff8cf29.jpg and /dev/null differ diff --git a/pics/9b5e0fa0-9274-4219-a3a9-84fbb509c735.jpg b/pics/9b5e0fa0-9274-4219-a3a9-84fbb509c735.jpg deleted file mode 100644 index dd41f3c1..00000000 Binary files a/pics/9b5e0fa0-9274-4219-a3a9-84fbb509c735.jpg and /dev/null differ diff --git a/pics/9c997ac5-c8a7-44fe-bf45-2c10eb773e53.jpg b/pics/9c997ac5-c8a7-44fe-bf45-2c10eb773e53.jpg deleted file mode 100644 index 0e3f2bb3..00000000 Binary files a/pics/9c997ac5-c8a7-44fe-bf45-2c10eb773e53.jpg and /dev/null differ diff --git a/pics/9cd0ae20-4fb5-4017-a000-f7d3a0eb3529.png b/pics/9cd0ae20-4fb5-4017-a000-f7d3a0eb3529.png new file mode 100644 index 00000000..49da824e Binary files /dev/null and b/pics/9cd0ae20-4fb5-4017-a000-f7d3a0eb3529.png differ diff --git a/pics/9cd97f41-fd9c-405c-aca1-9c82d24e20dc.png b/pics/9cd97f41-fd9c-405c-aca1-9c82d24e20dc.png deleted file mode 100644 index 83b3fa61..00000000 Binary files a/pics/9cd97f41-fd9c-405c-aca1-9c82d24e20dc.png and /dev/null differ diff --git a/pics/9d0a637c-6a8f-4f5a-99b9-fdcfa26793ff.png b/pics/9d0a637c-6a8f-4f5a-99b9-fdcfa26793ff.png new file mode 100644 index 00000000..34a8f664 Binary files /dev/null and b/pics/9d0a637c-6a8f-4f5a-99b9-fdcfa26793ff.png differ diff --git a/pics/9d2226dc-c4a3-40ec-9b3e-a46bf86af499.png b/pics/9d2226dc-c4a3-40ec-9b3e-a46bf86af499.png deleted file mode 100644 index cbef4f66..00000000 Binary files a/pics/9d2226dc-c4a3-40ec-9b3e-a46bf86af499.png and /dev/null differ diff --git a/pics/9dbb5fc2-936b-4c6d-b3a7-9617aae45080.jpg b/pics/9dbb5fc2-936b-4c6d-b3a7-9617aae45080.jpg deleted file mode 100644 index 05f71904..00000000 Binary files a/pics/9dbb5fc2-936b-4c6d-b3a7-9617aae45080.jpg and /dev/null differ diff --git a/pics/9e5e3cc6-3107-4051-b584-8ff077638fe6.png b/pics/9e5e3cc6-3107-4051-b584-8ff077638fe6.png deleted file mode 100644 index a3e8506b..00000000 Binary files a/pics/9e5e3cc6-3107-4051-b584-8ff077638fe6.png and /dev/null differ diff --git a/pics/9ecaebee-670e-4cb2-9cdb-3029c00f33bd.png b/pics/9ecaebee-670e-4cb2-9cdb-3029c00f33bd.png deleted file mode 100644 index 6db44a1e..00000000 Binary files a/pics/9ecaebee-670e-4cb2-9cdb-3029c00f33bd.png and /dev/null differ diff --git a/pics/9ee83c8c-1165-476c-85a6-e6e434e5307a.jpg b/pics/9ee83c8c-1165-476c-85a6-e6e434e5307a.jpg deleted file mode 100644 index 644fd766..00000000 Binary files a/pics/9ee83c8c-1165-476c-85a6-e6e434e5307a.jpg and /dev/null differ diff --git a/pics/CLone_20_281_29.png b/pics/CLone_20_281_29.png deleted file mode 100644 index 8b5b9308..00000000 Binary files a/pics/CLone_20_281_29.png and /dev/null differ diff --git a/pics/ClienteServidorSockets.jpg b/pics/ClienteServidorSockets.jpg deleted file mode 100644 index 18976fb3..00000000 Binary files a/pics/ClienteServidorSockets.jpg and /dev/null differ diff --git a/pics/GUID_Partition_Table_Scheme.svg.png b/pics/GUID_Partition_Table_Scheme.svg.png new file mode 100644 index 00000000..6638ae7a Binary files /dev/null and b/pics/GUID_Partition_Table_Scheme.svg.png differ diff --git a/pics/HTTP_RequestMessageExample.png b/pics/HTTP_RequestMessageExample.png new file mode 100644 index 00000000..8fd213cb Binary files /dev/null and b/pics/HTTP_RequestMessageExample.png differ diff --git a/pics/HTTP_ResponseMessageExample.png b/pics/HTTP_ResponseMessageExample.png new file mode 100644 index 00000000..1adf26c5 Binary files /dev/null and b/pics/HTTP_ResponseMessageExample.png differ diff --git a/pics/How-HTTPS-Works.png b/pics/How-HTTPS-Works.png new file mode 100644 index 00000000..c10605f7 Binary files /dev/null and b/pics/How-HTTPS-Works.png differ diff --git a/pics/JNIFigure1.gif b/pics/JNIFigure1.gif new file mode 100644 index 00000000..f47f289e Binary files /dev/null and b/pics/JNIFigure1.gif differ diff --git a/pics/JVM-Stack.png b/pics/JVM-Stack.png new file mode 100644 index 00000000..e55ccf9b Binary files /dev/null and b/pics/JVM-Stack.png differ diff --git a/pics/JVM-runtime-data-area.jpg b/pics/JVM-runtime-data-area.jpg new file mode 100644 index 00000000..88f8691f Binary files /dev/null and b/pics/JVM-runtime-data-area.jpg differ diff --git a/pics/MultiNode-SessionReplication.jpg b/pics/MultiNode-SessionReplication.jpg new file mode 100644 index 00000000..0223bd80 Binary files /dev/null and b/pics/MultiNode-SessionReplication.jpg differ diff --git a/pics/MultiNode-SpringSession.jpg b/pics/MultiNode-SpringSession.jpg new file mode 100644 index 00000000..38d56e2c Binary files /dev/null and b/pics/MultiNode-SpringSession.jpg differ diff --git a/pics/MultiNode-StickySessions.jpg b/pics/MultiNode-StickySessions.jpg new file mode 100644 index 00000000..a7e1c6aa Binary files /dev/null and b/pics/MultiNode-StickySessions.jpg differ diff --git a/pics/TIM截图20180227172950.png b/pics/TIM截图20180227172950.png deleted file mode 100644 index 1cb6971e..00000000 Binary files a/pics/TIM截图20180227172950.png and /dev/null differ diff --git a/pics/a01d1516-8168-461a-a24b-620b9cfc40f4.png b/pics/a01d1516-8168-461a-a24b-620b9cfc40f4.png new file mode 100644 index 00000000..4b9b4b48 Binary files /dev/null and b/pics/a01d1516-8168-461a-a24b-620b9cfc40f4.png differ diff --git a/pics/a0339a9f-f44f-4e37-a37f-169bc735536d.jpg b/pics/a0339a9f-f44f-4e37-a37f-169bc735536d.jpg deleted file mode 100644 index a20282eb..00000000 Binary files a/pics/a0339a9f-f44f-4e37-a37f-169bc735536d.jpg and /dev/null differ diff --git a/pics/a1ced733-02f5-4091-8f5a-ab9b4e5a3525.png b/pics/a1ced733-02f5-4091-8f5a-ab9b4e5a3525.png deleted file mode 100644 index fa09f82f..00000000 Binary files a/pics/a1ced733-02f5-4091-8f5a-ab9b4e5a3525.png and /dev/null differ diff --git a/pics/a259182b-91b7-4b63-98c9-9d9cc6e199d4.png b/pics/a259182b-91b7-4b63-98c9-9d9cc6e199d4.png deleted file mode 100644 index 42be8f02..00000000 Binary files a/pics/a259182b-91b7-4b63-98c9-9d9cc6e199d4.png and /dev/null differ diff --git a/pics/a2670745-a7b1-497b-90a4-dbddc4e2006d.jpg b/pics/a2670745-a7b1-497b-90a4-dbddc4e2006d.jpg deleted file mode 100644 index 66026828..00000000 Binary files a/pics/a2670745-a7b1-497b-90a4-dbddc4e2006d.jpg and /dev/null differ diff --git a/pics/a2d13178-f1ef-4811-a240-1fe95b55b1eb.png b/pics/a2d13178-f1ef-4811-a240-1fe95b55b1eb.png new file mode 100644 index 00000000..edc6840a Binary files /dev/null and b/pics/a2d13178-f1ef-4811-a240-1fe95b55b1eb.png differ diff --git a/pics/a314bb79-5b18-4e63-a976-3448bffa6f1b.png b/pics/a314bb79-5b18-4e63-a976-3448bffa6f1b.png new file mode 100644 index 00000000..1a5a6474 Binary files /dev/null and b/pics/a314bb79-5b18-4e63-a976-3448bffa6f1b.png differ diff --git a/pics/a3253deb-8d21-40a1-aae4-7d178e4aa319.jpg b/pics/a3253deb-8d21-40a1-aae4-7d178e4aa319.jpg new file mode 100644 index 00000000..23258afa Binary files /dev/null and b/pics/a3253deb-8d21-40a1-aae4-7d178e4aa319.jpg differ diff --git a/pics/a451b523-7e24-4fae-8e35-c46b14beed68.png b/pics/a451b523-7e24-4fae-8e35-c46b14beed68.png deleted file mode 100644 index 45ca0ed2..00000000 Binary files a/pics/a451b523-7e24-4fae-8e35-c46b14beed68.png and /dev/null differ diff --git a/pics/a574c5c8-142d-4f6c-8fd7-8ca9b16b7546.png b/pics/a574c5c8-142d-4f6c-8fd7-8ca9b16b7546.png deleted file mode 100644 index 0b90bef5..00000000 Binary files a/pics/a574c5c8-142d-4f6c-8fd7-8ca9b16b7546.png and /dev/null differ diff --git a/pics/a57a6fc8-c5e9-456c-80ff-a5139dda4b6e.png b/pics/a57a6fc8-c5e9-456c-80ff-a5139dda4b6e.png new file mode 100644 index 00000000..384e7aa6 Binary files /dev/null and b/pics/a57a6fc8-c5e9-456c-80ff-a5139dda4b6e.png differ diff --git a/pics/a5fa89e7-54b9-4e2f-8c48-a35712d7b2f5.jpg b/pics/a5fa89e7-54b9-4e2f-8c48-a35712d7b2f5.jpg deleted file mode 100644 index 4af8eb07..00000000 Binary files a/pics/a5fa89e7-54b9-4e2f-8c48-a35712d7b2f5.jpg and /dev/null differ diff --git a/pics/a6026bb4-3daf-439f-b1ec-a5a24e19d2fb.jpg b/pics/a6026bb4-3daf-439f-b1ec-a5a24e19d2fb.jpg new file mode 100644 index 00000000..4ff577e2 Binary files /dev/null and b/pics/a6026bb4-3daf-439f-b1ec-a5a24e19d2fb.jpg differ diff --git a/pics/a69af9bb-b5ad-4896-862d-697e5ee4feb1.png b/pics/a69af9bb-b5ad-4896-862d-697e5ee4feb1.png deleted file mode 100644 index e07677bc..00000000 Binary files a/pics/a69af9bb-b5ad-4896-862d-697e5ee4feb1.png and /dev/null differ diff --git a/pics/a758c8b2-0ac7-438f-90c2-3923ffad6328.png b/pics/a758c8b2-0ac7-438f-90c2-3923ffad6328.png deleted file mode 100644 index 355d5533..00000000 Binary files a/pics/a758c8b2-0ac7-438f-90c2-3923ffad6328.png and /dev/null differ diff --git a/pics/a797959a-0ed5-475b-8d97-df157c672019.jpg b/pics/a797959a-0ed5-475b-8d97-df157c672019.jpg deleted file mode 100644 index 18af09d8..00000000 Binary files a/pics/a797959a-0ed5-475b-8d97-df157c672019.jpg and /dev/null differ diff --git a/pics/a9098783-c24a-45b2-a226-725a59b6768e.png b/pics/a9098783-c24a-45b2-a226-725a59b6768e.png deleted file mode 100644 index a369861d..00000000 Binary files a/pics/a9098783-c24a-45b2-a226-725a59b6768e.png and /dev/null differ diff --git a/pics/a9339620-4689-414f-8e26-19821039614a.jpg b/pics/a9339620-4689-414f-8e26-19821039614a.jpg deleted file mode 100644 index a5c1634d..00000000 Binary files a/pics/a9339620-4689-414f-8e26-19821039614a.jpg and /dev/null differ diff --git a/pics/a9b6c1db-0f4a-4e91-8ac8-6b19bd106b51.png b/pics/a9b6c1db-0f4a-4e91-8ac8-6b19bd106b51.png deleted file mode 100644 index d2f3fc46..00000000 Binary files a/pics/a9b6c1db-0f4a-4e91-8ac8-6b19bd106b51.png and /dev/null differ diff --git a/pics/a9b91b7d-65d7-4aa3-8ef6-21876b05ad16.png b/pics/a9b91b7d-65d7-4aa3-8ef6-21876b05ad16.png deleted file mode 100644 index 8a60458f..00000000 Binary files a/pics/a9b91b7d-65d7-4aa3-8ef6-21876b05ad16.png and /dev/null differ diff --git a/pics/aa202729-769d-4d2a-b103-f45b412ceaf4.jpg b/pics/aa202729-769d-4d2a-b103-f45b412ceaf4.jpg deleted file mode 100644 index a0c32b7a..00000000 Binary files a/pics/aa202729-769d-4d2a-b103-f45b412ceaf4.jpg and /dev/null differ diff --git a/pics/aa20c123-b6b5-432a-83d3-45dc39172192.jpg b/pics/aa20c123-b6b5-432a-83d3-45dc39172192.jpg deleted file mode 100644 index 3c2c7ffe..00000000 Binary files a/pics/aa20c123-b6b5-432a-83d3-45dc39172192.jpg and /dev/null differ diff --git a/pics/aa29cc88-7256-4399-8c7f-3cf4a6489559.png b/pics/aa29cc88-7256-4399-8c7f-3cf4a6489559.png new file mode 100644 index 00000000..9b93237e Binary files /dev/null and b/pics/aa29cc88-7256-4399-8c7f-3cf4a6489559.png differ diff --git a/pics/aa340e1a-f366-436b-a5a5-29a90425c10d.png b/pics/aa340e1a-f366-436b-a5a5-29a90425c10d.png deleted file mode 100644 index 5d866289..00000000 Binary files a/pics/aa340e1a-f366-436b-a5a5-29a90425c10d.png and /dev/null differ diff --git a/pics/aa42f9c6-ad7a-48f4-8e8b-f3b6de3feaec.png b/pics/aa42f9c6-ad7a-48f4-8e8b-f3b6de3feaec.png new file mode 100644 index 00000000..cdb78c11 Binary files /dev/null and b/pics/aa42f9c6-ad7a-48f4-8e8b-f3b6de3feaec.png differ diff --git a/pics/aa62b91f-3540-4a28-8ea9-045f62ab3bcc.png b/pics/aa62b91f-3540-4a28-8ea9-045f62ab3bcc.png deleted file mode 100644 index ddbc1204..00000000 Binary files a/pics/aa62b91f-3540-4a28-8ea9-045f62ab3bcc.png and /dev/null differ diff --git a/pics/aaa40d0a-0aff-4161-adde-8d7bf56dc4b9.jpg b/pics/aaa40d0a-0aff-4161-adde-8d7bf56dc4b9.jpg deleted file mode 100644 index f34d7ec5..00000000 Binary files a/pics/aaa40d0a-0aff-4161-adde-8d7bf56dc4b9.jpg and /dev/null differ diff --git a/pics/ab77240d-7338-4547-9183-00215e7220ec.png b/pics/ab77240d-7338-4547-9183-00215e7220ec.png new file mode 100644 index 00000000..d57d91ce Binary files /dev/null and b/pics/ab77240d-7338-4547-9183-00215e7220ec.png differ diff --git a/pics/acc42b0f-10ba-4fa2-8694-cf2aab1fb434.jpg b/pics/acc42b0f-10ba-4fa2-8694-cf2aab1fb434.jpg deleted file mode 100644 index 24a07a5c..00000000 Binary files a/pics/acc42b0f-10ba-4fa2-8694-cf2aab1fb434.jpg and /dev/null differ diff --git a/pics/ae1f3f27-cb47-436d-b8a2-185618851b57.png b/pics/ae1f3f27-cb47-436d-b8a2-185618851b57.png deleted file mode 100644 index 4e0f5c93..00000000 Binary files a/pics/ae1f3f27-cb47-436d-b8a2-185618851b57.png and /dev/null differ diff --git a/pics/ae3fc93a-44d5-4beb-b05a-874bd9c0a657.png b/pics/ae3fc93a-44d5-4beb-b05a-874bd9c0a657.png new file mode 100644 index 00000000..ea23c7db Binary files /dev/null and b/pics/ae3fc93a-44d5-4beb-b05a-874bd9c0a657.png differ diff --git a/pics/af4639f9-af54-4400-aaf5-4e261d96ace7.png b/pics/af4639f9-af54-4400-aaf5-4e261d96ace7.png new file mode 100644 index 00000000..8de4fa4d Binary files /dev/null and b/pics/af4639f9-af54-4400-aaf5-4e261d96ace7.png differ diff --git a/pics/b001fa64-307c-49af-b4b2-2043fc26154e.png b/pics/b001fa64-307c-49af-b4b2-2043fc26154e.png deleted file mode 100644 index 8575c0d2..00000000 Binary files a/pics/b001fa64-307c-49af-b4b2-2043fc26154e.png and /dev/null differ diff --git a/pics/b02a5492-5dcf-4a69-9b5b-c2298b2cb81c.jpg b/pics/b02a5492-5dcf-4a69-9b5b-c2298b2cb81c.jpg deleted file mode 100644 index b951d9c0..00000000 Binary files a/pics/b02a5492-5dcf-4a69-9b5b-c2298b2cb81c.jpg and /dev/null differ diff --git a/pics/b0748916-1acd-4138-b24c-69326cb452fe.jpg b/pics/b0748916-1acd-4138-b24c-69326cb452fe.jpg deleted file mode 100644 index 350f3aa3..00000000 Binary files a/pics/b0748916-1acd-4138-b24c-69326cb452fe.jpg and /dev/null differ diff --git a/pics/b0d94736-e157-4886-aff2-c303735b0a24.jpg b/pics/b0d94736-e157-4886-aff2-c303735b0a24.jpg deleted file mode 100644 index 96ec26a7..00000000 Binary files a/pics/b0d94736-e157-4886-aff2-c303735b0a24.jpg and /dev/null differ diff --git a/pics/b18d679b-c8e2-4564-88ee-7600090e46da.jpg b/pics/b18d679b-c8e2-4564-88ee-7600090e46da.jpg deleted file mode 100644 index d8194ab1..00000000 Binary files a/pics/b18d679b-c8e2-4564-88ee-7600090e46da.jpg and /dev/null differ diff --git a/pics/b1b4cf7d-c54a-4ff1-9741-cd2eea331123.jpg b/pics/b1b4cf7d-c54a-4ff1-9741-cd2eea331123.jpg deleted file mode 100644 index fca884c9..00000000 Binary files a/pics/b1b4cf7d-c54a-4ff1-9741-cd2eea331123.jpg and /dev/null differ diff --git a/pics/b202eeb9-5e84-4dfb-a6a1-4f4b7ed5d3e4.jpg b/pics/b202eeb9-5e84-4dfb-a6a1-4f4b7ed5d3e4.jpg deleted file mode 100644 index 4d844450..00000000 Binary files a/pics/b202eeb9-5e84-4dfb-a6a1-4f4b7ed5d3e4.jpg and /dev/null differ diff --git a/pics/b242fafc-5945-42a8-805e-6e3f1f2f89b4.jpg b/pics/b242fafc-5945-42a8-805e-6e3f1f2f89b4.jpg deleted file mode 100644 index 13d2bf61..00000000 Binary files a/pics/b242fafc-5945-42a8-805e-6e3f1f2f89b4.jpg and /dev/null differ diff --git a/pics/b29f8971-9cb8-480d-b986-0e60c2ece069.png b/pics/b29f8971-9cb8-480d-b986-0e60c2ece069.png new file mode 100644 index 00000000..65f73b0e Binary files /dev/null and b/pics/b29f8971-9cb8-480d-b986-0e60c2ece069.png differ diff --git a/pics/b2b6253c-c701-4b30-aff4-bc3c713542a7.jpg b/pics/b2b6253c-c701-4b30-aff4-bc3c713542a7.jpg deleted file mode 100644 index e4036e10..00000000 Binary files a/pics/b2b6253c-c701-4b30-aff4-bc3c713542a7.jpg and /dev/null differ diff --git a/pics/b418ca51-f005-4510-b7ad-f092eb6aeb24.png b/pics/b418ca51-f005-4510-b7ad-f092eb6aeb24.png new file mode 100644 index 00000000..07157513 Binary files /dev/null and b/pics/b418ca51-f005-4510-b7ad-f092eb6aeb24.png differ diff --git a/pics/b4252c85-6fb0-4995-9a68-a1a5925fbdb1.png b/pics/b4252c85-6fb0-4995-9a68-a1a5925fbdb1.png new file mode 100644 index 00000000..59ea02cc Binary files /dev/null and b/pics/b4252c85-6fb0-4995-9a68-a1a5925fbdb1.png differ diff --git a/pics/b43437dd-16b6-4023-bdda-d6f7d6db762e.png b/pics/b43437dd-16b6-4023-bdda-d6f7d6db762e.png deleted file mode 100644 index 5dcc9708..00000000 Binary files a/pics/b43437dd-16b6-4023-bdda-d6f7d6db762e.png and /dev/null differ diff --git a/pics/b488282d-bfe0-464f-9e91-1f5b83a975bd.jpg b/pics/b488282d-bfe0-464f-9e91-1f5b83a975bd.jpg deleted file mode 100644 index c1427eb3..00000000 Binary files a/pics/b488282d-bfe0-464f-9e91-1f5b83a975bd.jpg and /dev/null differ diff --git a/pics/b56ef52e-3d0f-4cdd-97dc-eaed893444a5.jpg b/pics/b56ef52e-3d0f-4cdd-97dc-eaed893444a5.jpg deleted file mode 100644 index 1acf8feb..00000000 Binary files a/pics/b56ef52e-3d0f-4cdd-97dc-eaed893444a5.jpg and /dev/null differ diff --git a/pics/b5af9ee6-97e6-446b-9551-6dfe96770d1a.png b/pics/b5af9ee6-97e6-446b-9551-6dfe96770d1a.png deleted file mode 100644 index 35c375b1..00000000 Binary files a/pics/b5af9ee6-97e6-446b-9551-6dfe96770d1a.png and /dev/null differ diff --git a/pics/b5c78914-066f-42be-ad1a-1c9f72aa9093.png b/pics/b5c78914-066f-42be-ad1a-1c9f72aa9093.png deleted file mode 100644 index b8950c27..00000000 Binary files a/pics/b5c78914-066f-42be-ad1a-1c9f72aa9093.png and /dev/null differ diff --git a/pics/b5fed547-a989-4ead-81d5-ea72660faf99.png b/pics/b5fed547-a989-4ead-81d5-ea72660faf99.png deleted file mode 100644 index cb04f7cb..00000000 Binary files a/pics/b5fed547-a989-4ead-81d5-ea72660faf99.png and /dev/null differ diff --git a/pics/b69d7184-ab62-4957-ba29-fb4fa25f9b65.jpg b/pics/b69d7184-ab62-4957-ba29-fb4fa25f9b65.jpg deleted file mode 100644 index cae4d127..00000000 Binary files a/pics/b69d7184-ab62-4957-ba29-fb4fa25f9b65.jpg and /dev/null differ diff --git a/pics/b6a678c0-c875-4038-afba-301846620786.jpg b/pics/b6a678c0-c875-4038-afba-301846620786.jpg deleted file mode 100644 index c3194070..00000000 Binary files a/pics/b6a678c0-c875-4038-afba-301846620786.jpg and /dev/null differ diff --git a/pics/b6acae0d-7148-41de-adc3-ff5ff8dca3ae.jpg b/pics/b6acae0d-7148-41de-adc3-ff5ff8dca3ae.jpg deleted file mode 100644 index 852e4fdd..00000000 Binary files a/pics/b6acae0d-7148-41de-adc3-ff5ff8dca3ae.jpg and /dev/null differ diff --git a/pics/b750eb3e-8a80-475c-95df-2e971b277603.jpg b/pics/b750eb3e-8a80-475c-95df-2e971b277603.jpg deleted file mode 100644 index 56868362..00000000 Binary files a/pics/b750eb3e-8a80-475c-95df-2e971b277603.jpg and /dev/null differ diff --git a/pics/b7b1f5c6-ff8a-4353-8060-44bbc4b9e02e.jpg b/pics/b7b1f5c6-ff8a-4353-8060-44bbc4b9e02e.jpg deleted file mode 100644 index 1dd8427d..00000000 Binary files a/pics/b7b1f5c6-ff8a-4353-8060-44bbc4b9e02e.jpg and /dev/null differ diff --git a/pics/b84ba6fb-312b-4e69-8c77-fb6eb6fb38d4.png b/pics/b84ba6fb-312b-4e69-8c77-fb6eb6fb38d4.png new file mode 100644 index 00000000..dc44da3d Binary files /dev/null and b/pics/b84ba6fb-312b-4e69-8c77-fb6eb6fb38d4.png differ diff --git a/pics/b8ceb9db-180e-4d01-932c-593fa2a6f515.jpg b/pics/b8ceb9db-180e-4d01-932c-593fa2a6f515.jpg deleted file mode 100644 index 69af8dfb..00000000 Binary files a/pics/b8ceb9db-180e-4d01-932c-593fa2a6f515.jpg and /dev/null differ diff --git a/pics/b97958dd-3e43-45f7-97f5-3ec20f3f8b88.jpg b/pics/b97958dd-3e43-45f7-97f5-3ec20f3f8b88.jpg deleted file mode 100644 index 40f51aa8..00000000 Binary files a/pics/b97958dd-3e43-45f7-97f5-3ec20f3f8b88.jpg and /dev/null differ diff --git a/pics/b9d79a5a-e7af-499b-b989-f10483e71b8b.jpg b/pics/b9d79a5a-e7af-499b-b989-f10483e71b8b.jpg new file mode 100644 index 00000000..b7502831 Binary files /dev/null and b/pics/b9d79a5a-e7af-499b-b989-f10483e71b8b.jpg differ diff --git a/pics/b9e9ae8c-e216-4c01-b267-a50dbeb98fa4.jpg b/pics/b9e9ae8c-e216-4c01-b267-a50dbeb98fa4.jpg deleted file mode 100644 index 29940be2..00000000 Binary files a/pics/b9e9ae8c-e216-4c01-b267-a50dbeb98fa4.jpg and /dev/null differ diff --git a/pics/ba6ae411-82da-4d86-a434-6776d1731e8e.jpg b/pics/ba6ae411-82da-4d86-a434-6776d1731e8e.jpg deleted file mode 100644 index c1013f01..00000000 Binary files a/pics/ba6ae411-82da-4d86-a434-6776d1731e8e.jpg and /dev/null differ diff --git a/pics/bccb799f-56e2-4356-95f0-a9ea05b0de2a.jpg b/pics/bccb799f-56e2-4356-95f0-a9ea05b0de2a.jpg deleted file mode 100644 index 30b2ea88..00000000 Binary files a/pics/bccb799f-56e2-4356-95f0-a9ea05b0de2a.jpg and /dev/null differ diff --git a/pics/bd6c05f3-02ee-4c8a-b374-40c87154a898.jpg b/pics/bd6c05f3-02ee-4c8a-b374-40c87154a898.jpg deleted file mode 100644 index 94277944..00000000 Binary files a/pics/bd6c05f3-02ee-4c8a-b374-40c87154a898.jpg and /dev/null differ diff --git a/pics/be53c00b-2534-4dc6-ad03-c55995c47db9.jpg b/pics/be53c00b-2534-4dc6-ad03-c55995c47db9.jpg deleted file mode 100644 index 515ffd64..00000000 Binary files a/pics/be53c00b-2534-4dc6-ad03-c55995c47db9.jpg and /dev/null differ diff --git a/pics/be7dca03-12ec-456b-8b54-b1b3161f5531.png b/pics/be7dca03-12ec-456b-8b54-b1b3161f5531.png new file mode 100644 index 00000000..2c2214de Binary files /dev/null and b/pics/be7dca03-12ec-456b-8b54-b1b3161f5531.png differ diff --git a/pics/bed9d745-f971-405d-ba57-bcfa7986c8bd.png b/pics/bed9d745-f971-405d-ba57-bcfa7986c8bd.png deleted file mode 100644 index f1f9adba..00000000 Binary files a/pics/bed9d745-f971-405d-ba57-bcfa7986c8bd.png and /dev/null differ diff --git a/pics/bf0ff9fc-467e-4a3f-8922-115ba2c55bde.png b/pics/bf0ff9fc-467e-4a3f-8922-115ba2c55bde.png new file mode 100644 index 00000000..69222ad8 Binary files /dev/null and b/pics/bf0ff9fc-467e-4a3f-8922-115ba2c55bde.png differ diff --git a/pics/bf4ed077-d481-4db7-9e7a-85d841a5a8c3.jpg b/pics/bf4ed077-d481-4db7-9e7a-85d841a5a8c3.jpg deleted file mode 100644 index a20751ad..00000000 Binary files a/pics/bf4ed077-d481-4db7-9e7a-85d841a5a8c3.jpg and /dev/null differ diff --git a/pics/bfbb11e2-d208-4efa-b97b-24cd40467cd8.png b/pics/bfbb11e2-d208-4efa-b97b-24cd40467cd8.png new file mode 100644 index 00000000..b9d9dba2 Binary files /dev/null and b/pics/bfbb11e2-d208-4efa-b97b-24cd40467cd8.png differ diff --git a/pics/c02a83b8-a6b9-4d00-a509-6f0516beaf5e.png b/pics/c02a83b8-a6b9-4d00-a509-6f0516beaf5e.png deleted file mode 100644 index 52f44ea4..00000000 Binary files a/pics/c02a83b8-a6b9-4d00-a509-6f0516beaf5e.png and /dev/null differ diff --git a/pics/c07035c3-a9ba-4508-8e3c-d8ae4c6ee9ee.jpg b/pics/c07035c3-a9ba-4508-8e3c-d8ae4c6ee9ee.jpg deleted file mode 100644 index acb38080..00000000 Binary files a/pics/c07035c3-a9ba-4508-8e3c-d8ae4c6ee9ee.jpg and /dev/null differ diff --git a/pics/c11f5e3a-7a28-4db7-87b1-29ba1a2f2d72.jpg b/pics/c11f5e3a-7a28-4db7-87b1-29ba1a2f2d72.jpg deleted file mode 100644 index 1bc52926..00000000 Binary files a/pics/c11f5e3a-7a28-4db7-87b1-29ba1a2f2d72.jpg and /dev/null differ diff --git a/pics/c23957e9-a572-44f8-be15-f306c8b92722.jpg b/pics/c23957e9-a572-44f8-be15-f306c8b92722.jpg new file mode 100644 index 00000000..2732c6d7 Binary files /dev/null and b/pics/c23957e9-a572-44f8-be15-f306c8b92722.jpg differ diff --git a/pics/c24ad1af-d81b-409f-b4eb-62db9002d525.png b/pics/c24ad1af-d81b-409f-b4eb-62db9002d525.png deleted file mode 100644 index ae164aad..00000000 Binary files a/pics/c24ad1af-d81b-409f-b4eb-62db9002d525.png and /dev/null differ diff --git a/pics/c28fd93a-0d55-4a19-810f-72652feee00d.jpg b/pics/c28fd93a-0d55-4a19-810f-72652feee00d.jpg deleted file mode 100644 index 7f198507..00000000 Binary files a/pics/c28fd93a-0d55-4a19-810f-72652feee00d.jpg and /dev/null differ diff --git a/pics/c3369072-c740-43b0-b276-202bd1d3960d.jpg b/pics/c3369072-c740-43b0-b276-202bd1d3960d.jpg new file mode 100644 index 00000000..17a2e9bf Binary files /dev/null and b/pics/c3369072-c740-43b0-b276-202bd1d3960d.jpg differ diff --git a/pics/c3ca36b2-8459-4cf1-98b0-cc95a0e94f20.jpg b/pics/c3ca36b2-8459-4cf1-98b0-cc95a0e94f20.jpg deleted file mode 100644 index 94d726a1..00000000 Binary files a/pics/c3ca36b2-8459-4cf1-98b0-cc95a0e94f20.jpg and /dev/null differ diff --git a/pics/c41d3977-e0e7-4ee4-93e1-d84f1ae3e20e.jpg b/pics/c41d3977-e0e7-4ee4-93e1-d84f1ae3e20e.jpg deleted file mode 100644 index bf70b25a..00000000 Binary files a/pics/c41d3977-e0e7-4ee4-93e1-d84f1ae3e20e.jpg and /dev/null differ diff --git a/pics/c470eb9b-fb05-45c5-8bb7-1057dc3c16de.jpg b/pics/c470eb9b-fb05-45c5-8bb7-1057dc3c16de.jpg deleted file mode 100644 index ed426067..00000000 Binary files a/pics/c470eb9b-fb05-45c5-8bb7-1057dc3c16de.jpg and /dev/null differ diff --git a/pics/c484b07d-be3d-4699-9e28-f035de8a274c.jpg b/pics/c484b07d-be3d-4699-9e28-f035de8a274c.jpg deleted file mode 100644 index 4c90b9d8..00000000 Binary files a/pics/c484b07d-be3d-4699-9e28-f035de8a274c.jpg and /dev/null differ diff --git a/pics/c5022dd3-be22-4250-b9f6-38ae984a04d7.jpg b/pics/c5022dd3-be22-4250-b9f6-38ae984a04d7.jpg new file mode 100644 index 00000000..3eb79406 Binary files /dev/null and b/pics/c5022dd3-be22-4250-b9f6-38ae984a04d7.jpg differ diff --git a/pics/c50d230c-8b89-4644-8f62-8708d03aac5b.jpg b/pics/c50d230c-8b89-4644-8f62-8708d03aac5b.jpg deleted file mode 100644 index c1afec59..00000000 Binary files a/pics/c50d230c-8b89-4644-8f62-8708d03aac5b.jpg and /dev/null differ diff --git a/pics/c5409a64-81fc-48c1-9790-ffa0afdb7bf5.png b/pics/c5409a64-81fc-48c1-9790-ffa0afdb7bf5.png deleted file mode 100644 index 8bfaa053..00000000 Binary files a/pics/c5409a64-81fc-48c1-9790-ffa0afdb7bf5.png and /dev/null differ diff --git a/pics/c634b5ed-a14b-4302-b40e-3ee387dd3c8a.jpg b/pics/c634b5ed-a14b-4302-b40e-3ee387dd3c8a.jpg deleted file mode 100644 index f3fffbc3..00000000 Binary files a/pics/c634b5ed-a14b-4302-b40e-3ee387dd3c8a.jpg and /dev/null differ diff --git a/pics/c64f91e2-f5a8-436b-8663-b8f3fba3e098.png b/pics/c64f91e2-f5a8-436b-8663-b8f3fba3e098.png deleted file mode 100644 index 28e21068..00000000 Binary files a/pics/c64f91e2-f5a8-436b-8663-b8f3fba3e098.png and /dev/null differ diff --git a/pics/c73a0b78-5f46-4d2d-a009-dab2a999b5d8.jpg b/pics/c73a0b78-5f46-4d2d-a009-dab2a999b5d8.jpg deleted file mode 100644 index adcb251b..00000000 Binary files a/pics/c73a0b78-5f46-4d2d-a009-dab2a999b5d8.jpg and /dev/null differ diff --git a/pics/c7665f73-c52f-4ce4-aed3-592bbd76265b.png b/pics/c7665f73-c52f-4ce4-aed3-592bbd76265b.png deleted file mode 100644 index 3994ee98..00000000 Binary files a/pics/c7665f73-c52f-4ce4-aed3-592bbd76265b.png and /dev/null differ diff --git a/pics/c77b6a18-dfac-42a2-ac89-7e99481275dc.jpg b/pics/c77b6a18-dfac-42a2-ac89-7e99481275dc.jpg deleted file mode 100644 index 71587795..00000000 Binary files a/pics/c77b6a18-dfac-42a2-ac89-7e99481275dc.jpg and /dev/null differ diff --git a/pics/c7b9b4c8-83d1-4eb0-8408-ea6576a9ed90.png b/pics/c7b9b4c8-83d1-4eb0-8408-ea6576a9ed90.png deleted file mode 100644 index 283dcd28..00000000 Binary files a/pics/c7b9b4c8-83d1-4eb0-8408-ea6576a9ed90.png and /dev/null differ diff --git a/pics/c7d4956c-9988-4a10-a704-28fdae7f3d28.png b/pics/c7d4956c-9988-4a10-a704-28fdae7f3d28.png new file mode 100644 index 00000000..56510359 Binary files /dev/null and b/pics/c7d4956c-9988-4a10-a704-28fdae7f3d28.png differ diff --git a/pics/c8637fd2-3aaa-46c4-b7d9-f24d3fa04781.jpg b/pics/c8637fd2-3aaa-46c4-b7d9-f24d3fa04781.jpg deleted file mode 100644 index 68fed2a3..00000000 Binary files a/pics/c8637fd2-3aaa-46c4-b7d9-f24d3fa04781.jpg and /dev/null differ diff --git a/pics/c8cdfa3d-d610-4731-9d89-564252378e7d.png b/pics/c8cdfa3d-d610-4731-9d89-564252378e7d.png deleted file mode 100644 index 5e395a29..00000000 Binary files a/pics/c8cdfa3d-d610-4731-9d89-564252378e7d.png and /dev/null differ diff --git a/pics/c8d18ca9-0b09-441a-9a0c-fb063630d708.png b/pics/c8d18ca9-0b09-441a-9a0c-fb063630d708.png new file mode 100644 index 00000000..514ab054 Binary files /dev/null and b/pics/c8d18ca9-0b09-441a-9a0c-fb063630d708.png differ diff --git a/pics/c95a18ae-4b97-4fa9-9806-d9ed7b345b42.png b/pics/c95a18ae-4b97-4fa9-9806-d9ed7b345b42.png deleted file mode 100644 index a7e3e6c3..00000000 Binary files a/pics/c95a18ae-4b97-4fa9-9806-d9ed7b345b42.png and /dev/null differ diff --git a/pics/c9a1de44-b1c0-4d13-a654-827d4ef8a723.png b/pics/c9a1de44-b1c0-4d13-a654-827d4ef8a723.png new file mode 100644 index 00000000..f9ad721b Binary files /dev/null and b/pics/c9a1de44-b1c0-4d13-a654-827d4ef8a723.png differ diff --git a/pics/ca3a793e-06e5-4ff3-b28e-a9c20540d164.png b/pics/ca3a793e-06e5-4ff3-b28e-a9c20540d164.png deleted file mode 100644 index d74230b1..00000000 Binary files a/pics/ca3a793e-06e5-4ff3-b28e-a9c20540d164.png and /dev/null differ diff --git a/pics/ca711108-e937-4d7d-99aa-61b325c61f1a.jpg b/pics/ca711108-e937-4d7d-99aa-61b325c61f1a.jpg deleted file mode 100644 index 52f0dec3..00000000 Binary files a/pics/ca711108-e937-4d7d-99aa-61b325c61f1a.jpg and /dev/null differ diff --git a/pics/cae894a9-2424-4de4-ab41-c15d7054a5e7.png b/pics/cae894a9-2424-4de4-ab41-c15d7054a5e7.png deleted file mode 100644 index 2000b9e2..00000000 Binary files a/pics/cae894a9-2424-4de4-ab41-c15d7054a5e7.png and /dev/null differ diff --git a/pics/cb0ed469-27ab-471b-a830-648b279103c8.png b/pics/cb0ed469-27ab-471b-a830-648b279103c8.png new file mode 100644 index 00000000..88c2ce86 Binary files /dev/null and b/pics/cb0ed469-27ab-471b-a830-648b279103c8.png differ diff --git a/pics/cbf50eb8-22b4-4528-a2e7-d187143d57f7.png b/pics/cbf50eb8-22b4-4528-a2e7-d187143d57f7.png new file mode 100644 index 00000000..27f2b742 Binary files /dev/null and b/pics/cbf50eb8-22b4-4528-a2e7-d187143d57f7.png differ diff --git a/pics/cc3d855a-4281-445b-8f83-8f855458de19.png b/pics/cc3d855a-4281-445b-8f83-8f855458de19.png deleted file mode 100644 index 95a60098..00000000 Binary files a/pics/cc3d855a-4281-445b-8f83-8f855458de19.png and /dev/null differ diff --git a/pics/cc7bfdeb-452e-4fae-9bc8-323338b0dedb.png b/pics/cc7bfdeb-452e-4fae-9bc8-323338b0dedb.png deleted file mode 100644 index 4ca86259..00000000 Binary files a/pics/cc7bfdeb-452e-4fae-9bc8-323338b0dedb.png and /dev/null differ diff --git a/pics/ce0fa5d0-866b-46e6-a873-8eb1f78c2882.jpg b/pics/ce0fa5d0-866b-46e6-a873-8eb1f78c2882.jpg deleted file mode 100644 index 218c91b1..00000000 Binary files a/pics/ce0fa5d0-866b-46e6-a873-8eb1f78c2882.jpg and /dev/null differ diff --git a/pics/ceee91c2-da26-4169-94c3-e4608b46b9ac.png b/pics/ceee91c2-da26-4169-94c3-e4608b46b9ac.png deleted file mode 100644 index 7f3890ad..00000000 Binary files a/pics/ceee91c2-da26-4169-94c3-e4608b46b9ac.png and /dev/null differ diff --git a/pics/cf08a51d-14c0-4bfc-863b-c8672d9c2b02.jpg b/pics/cf08a51d-14c0-4bfc-863b-c8672d9c2b02.jpg deleted file mode 100644 index b3b36b81..00000000 Binary files a/pics/cf08a51d-14c0-4bfc-863b-c8672d9c2b02.jpg and /dev/null differ diff --git a/pics/cfb05050-47aa-4fd1-86eb-a7c86320f81b.png b/pics/cfb05050-47aa-4fd1-86eb-a7c86320f81b.png deleted file mode 100644 index 7aa144dc..00000000 Binary files a/pics/cfb05050-47aa-4fd1-86eb-a7c86320f81b.png and /dev/null differ diff --git a/pics/class_loader_hierarchy.png b/pics/class_loader_hierarchy.png new file mode 100644 index 00000000..6f4baf6f Binary files /dev/null and b/pics/class_loader_hierarchy.png differ diff --git a/pics/cookiedata.png b/pics/cookiedata.png new file mode 100644 index 00000000..a425fca6 Binary files /dev/null and b/pics/cookiedata.png differ diff --git a/pics/d1f81ac3-9fdb-4371-a49d-ca84917aa89f.jpg b/pics/d1f81ac3-9fdb-4371-a49d-ca84917aa89f.jpg new file mode 100644 index 00000000..90953621 Binary files /dev/null and b/pics/d1f81ac3-9fdb-4371-a49d-ca84917aa89f.jpg differ diff --git a/pics/d206d090-d911-4263-a1fe-d6f63f5d1776.png b/pics/d206d090-d911-4263-a1fe-d6f63f5d1776.png deleted file mode 100644 index 1db3d0a5..00000000 Binary files a/pics/d206d090-d911-4263-a1fe-d6f63f5d1776.png and /dev/null differ diff --git a/pics/d2c55c84-aa1f-43c1-bd97-457bcb7816b3.png b/pics/d2c55c84-aa1f-43c1-bd97-457bcb7816b3.png deleted file mode 100644 index 6972e560..00000000 Binary files a/pics/d2c55c84-aa1f-43c1-bd97-457bcb7816b3.png and /dev/null differ diff --git a/pics/d301774f-e0d2-41f3-95f4-bfe39859b52e.jpg b/pics/d301774f-e0d2-41f3-95f4-bfe39859b52e.jpg deleted file mode 100644 index b2a49201..00000000 Binary files a/pics/d301774f-e0d2-41f3-95f4-bfe39859b52e.jpg and /dev/null differ diff --git a/pics/d3b06a52-cce1-4e05-aeff-1680a282b178.png b/pics/d3b06a52-cce1-4e05-aeff-1680a282b178.png deleted file mode 100644 index 7faf9bfb..00000000 Binary files a/pics/d3b06a52-cce1-4e05-aeff-1680a282b178.png and /dev/null differ diff --git a/pics/d4a05b9c-f423-4137-9510-b6851f089edb.jpg b/pics/d4a05b9c-f423-4137-9510-b6851f089edb.jpg deleted file mode 100644 index 3c5bd7c1..00000000 Binary files a/pics/d4a05b9c-f423-4137-9510-b6851f089edb.jpg and /dev/null differ diff --git a/pics/d4c3a4a1-0846-46ec-9cc3-eaddfca71254.jpg b/pics/d4c3a4a1-0846-46ec-9cc3-eaddfca71254.jpg new file mode 100644 index 00000000..bbc7f102 Binary files /dev/null and b/pics/d4c3a4a1-0846-46ec-9cc3-eaddfca71254.jpg differ diff --git a/pics/d4eef1e2-5703-4ca4-82ab-8dda93d6b81f.png b/pics/d4eef1e2-5703-4ca4-82ab-8dda93d6b81f.png new file mode 100644 index 00000000..4f62e6f5 Binary files /dev/null and b/pics/d4eef1e2-5703-4ca4-82ab-8dda93d6b81f.png differ diff --git a/pics/d5659bcf-5ddf-4692-bfe5-f6b480479120.png b/pics/d5659bcf-5ddf-4692-bfe5-f6b480479120.png deleted file mode 100644 index e3190ede..00000000 Binary files a/pics/d5659bcf-5ddf-4692-bfe5-f6b480479120.png and /dev/null differ diff --git a/pics/d7f6dec1-02b6-4969-b3ab-e01ee78659b9.png b/pics/d7f6dec1-02b6-4969-b3ab-e01ee78659b9.png new file mode 100644 index 00000000..2fa7a4e4 Binary files /dev/null and b/pics/d7f6dec1-02b6-4969-b3ab-e01ee78659b9.png differ diff --git a/pics/d8355d56-aa2b-4452-8001-8475cc095af1.jpg b/pics/d8355d56-aa2b-4452-8001-8475cc095af1.jpg deleted file mode 100644 index 96051090..00000000 Binary files a/pics/d8355d56-aa2b-4452-8001-8475cc095af1.jpg and /dev/null differ diff --git a/pics/d887219c-963a-4392-abe7-d3967546e96d.jpg b/pics/d887219c-963a-4392-abe7-d3967546e96d.jpg deleted file mode 100644 index 07fc20c9..00000000 Binary files a/pics/d887219c-963a-4392-abe7-d3967546e96d.jpg and /dev/null differ diff --git a/pics/d8f873fc-00bc-41ee-a87c-c1b4c0172844.png b/pics/d8f873fc-00bc-41ee-a87c-c1b4c0172844.png deleted file mode 100644 index 19cd1543..00000000 Binary files a/pics/d8f873fc-00bc-41ee-a87c-c1b4c0172844.png and /dev/null differ diff --git a/pics/d990c0e7-64d1-4ba3-8356-111bc91e53c5.png b/pics/d990c0e7-64d1-4ba3-8356-111bc91e53c5.png deleted file mode 100644 index cf31e608..00000000 Binary files a/pics/d990c0e7-64d1-4ba3-8356-111bc91e53c5.png and /dev/null differ diff --git a/pics/d99dc9e2-197c-4085-813d-7195da1c6762.png b/pics/d99dc9e2-197c-4085-813d-7195da1c6762.png new file mode 100644 index 00000000..a2c04eba Binary files /dev/null and b/pics/d99dc9e2-197c-4085-813d-7195da1c6762.png differ diff --git a/pics/d9efd6bd-3f34-497e-911c-16d5ea38ce88.png b/pics/d9efd6bd-3f34-497e-911c-16d5ea38ce88.png deleted file mode 100644 index 7a08e344..00000000 Binary files a/pics/d9efd6bd-3f34-497e-911c-16d5ea38ce88.png and /dev/null differ diff --git a/pics/da5dbeae-f247-400b-84d8-af48f0241bc9.png b/pics/da5dbeae-f247-400b-84d8-af48f0241bc9.png deleted file mode 100644 index b06e1ef1..00000000 Binary files a/pics/da5dbeae-f247-400b-84d8-af48f0241bc9.png and /dev/null differ diff --git a/pics/dac28811-79b6-4b75-bfa7-6b228e8ac3fb.png b/pics/dac28811-79b6-4b75-bfa7-6b228e8ac3fb.png deleted file mode 100644 index 467542c4..00000000 Binary files a/pics/dac28811-79b6-4b75-bfa7-6b228e8ac3fb.png and /dev/null differ diff --git a/pics/db4921d4-184b-48ba-a3cf-1d1141e3ba2d.png b/pics/db4921d4-184b-48ba-a3cf-1d1141e3ba2d.png new file mode 100644 index 00000000..f88f3b54 Binary files /dev/null and b/pics/db4921d4-184b-48ba-a3cf-1d1141e3ba2d.png differ diff --git a/pics/db54db2f-82b2-4222-8d63-e49a8a7fc966.png b/pics/db54db2f-82b2-4222-8d63-e49a8a7fc966.png new file mode 100644 index 00000000..23fb9449 Binary files /dev/null and b/pics/db54db2f-82b2-4222-8d63-e49a8a7fc966.png differ diff --git a/pics/dbb8516d-37ba-4e2c-b26b-eefd7de21b45.png b/pics/dbb8516d-37ba-4e2c-b26b-eefd7de21b45.png new file mode 100644 index 00000000..1ee66db8 Binary files /dev/null and b/pics/dbb8516d-37ba-4e2c-b26b-eefd7de21b45.png differ diff --git a/pics/dc00f70e-c5c8-4d20-baf1-2d70014a97e3.jpg b/pics/dc00f70e-c5c8-4d20-baf1-2d70014a97e3.jpg new file mode 100644 index 00000000..8090706b Binary files /dev/null and b/pics/dc00f70e-c5c8-4d20-baf1-2d70014a97e3.jpg differ diff --git a/pics/dc3e704c-7c57-42b8-93ea-ddd068665964.jpg b/pics/dc3e704c-7c57-42b8-93ea-ddd068665964.jpg deleted file mode 100644 index 558b8e11..00000000 Binary files a/pics/dc3e704c-7c57-42b8-93ea-ddd068665964.jpg and /dev/null differ diff --git a/pics/dc695f48-4189-4fc7-b950-ed25f6c1521708518830.jpg b/pics/dc695f48-4189-4fc7-b950-ed25f6c1521708518830.jpg deleted file mode 100644 index d612005a..00000000 Binary files a/pics/dc695f48-4189-4fc7-b950-ed25f6c1521708518830.jpg and /dev/null differ diff --git a/pics/dc695f48-4189-4fc7-b950-ed25f6c80f82.jpg b/pics/dc695f48-4189-4fc7-b950-ed25f6c80f82.jpg deleted file mode 100644 index d612005a..00000000 Binary files a/pics/dc695f48-4189-4fc7-b950-ed25f6c80f82.jpg and /dev/null differ diff --git a/pics/dc752c5b-bb59-4616-bf9c-21276690a24d.png b/pics/dc752c5b-bb59-4616-bf9c-21276690a24d.png new file mode 100644 index 00000000..66e77b6d Binary files /dev/null and b/pics/dc752c5b-bb59-4616-bf9c-21276690a24d.png differ diff --git a/pics/dcbd1473-96c3-4ace-8b69-2c9342615e7e.png b/pics/dcbd1473-96c3-4ace-8b69-2c9342615e7e.png deleted file mode 100644 index 0a0be2ea..00000000 Binary files a/pics/dcbd1473-96c3-4ace-8b69-2c9342615e7e.png and /dev/null differ diff --git a/pics/dcf265ad-fe35-424d-b4b7-d149cdf239f4.png b/pics/dcf265ad-fe35-424d-b4b7-d149cdf239f4.png deleted file mode 100644 index 8e598077..00000000 Binary files a/pics/dcf265ad-fe35-424d-b4b7-d149cdf239f4.png and /dev/null differ diff --git a/pics/dd137585-00bf-428e-8b88-475a8627e8bc.jpg b/pics/dd137585-00bf-428e-8b88-475a8627e8bc.jpg deleted file mode 100644 index 4bf55a05..00000000 Binary files a/pics/dd137585-00bf-428e-8b88-475a8627e8bc.jpg and /dev/null differ diff --git a/pics/dd782132-d830-4c55-9884-cfac0a541b8e.png b/pics/dd782132-d830-4c55-9884-cfac0a541b8e.png new file mode 100644 index 00000000..e338c1bb Binary files /dev/null and b/pics/dd782132-d830-4c55-9884-cfac0a541b8e.png differ diff --git a/pics/dda1608d-26e0-4f10-8327-a459969b150a.png b/pics/dda1608d-26e0-4f10-8327-a459969b150a.png new file mode 100644 index 00000000..7bab8f15 Binary files /dev/null and b/pics/dda1608d-26e0-4f10-8327-a459969b150a.png differ diff --git a/pics/ddcf2327-8d84-425d-8535-121a94bcb88d.jpg b/pics/ddcf2327-8d84-425d-8535-121a94bcb88d.jpg new file mode 100644 index 00000000..2a95d92d Binary files /dev/null and b/pics/ddcf2327-8d84-425d-8535-121a94bcb88d.jpg differ diff --git a/pics/ddf72ca9-c0be-49d7-ab81-57a99a974c8e.jpg b/pics/ddf72ca9-c0be-49d7-ab81-57a99a974c8e.jpg deleted file mode 100644 index a31c3adf..00000000 Binary files a/pics/ddf72ca9-c0be-49d7-ab81-57a99a974c8e.jpg and /dev/null differ diff --git a/pics/de1e46d2-748f-4da3-a29e-7de7bc840366.jpg b/pics/de1e46d2-748f-4da3-a29e-7de7bc840366.jpg deleted file mode 100644 index d5b8c80b..00000000 Binary files a/pics/de1e46d2-748f-4da3-a29e-7de7bc840366.jpg and /dev/null differ diff --git a/pics/de284292-b275-4454-8a98-f7e0de370a78.jpg b/pics/de284292-b275-4454-8a98-f7e0de370a78.jpg deleted file mode 100644 index b74e10bd..00000000 Binary files a/pics/de284292-b275-4454-8a98-f7e0de370a78.jpg and /dev/null differ diff --git a/pics/de7c5a31-55f5-4e9d-92ec-4ed5b2ec3828.jpg b/pics/de7c5a31-55f5-4e9d-92ec-4ed5b2ec3828.jpg deleted file mode 100644 index bb2f80cf..00000000 Binary files a/pics/de7c5a31-55f5-4e9d-92ec-4ed5b2ec3828.jpg and /dev/null differ diff --git a/pics/decb0936-e83c-4a55-840a-fe8aa101ac61.png b/pics/decb0936-e83c-4a55-840a-fe8aa101ac61.png deleted file mode 100644 index 1e7a8bca..00000000 Binary files a/pics/decb0936-e83c-4a55-840a-fe8aa101ac61.png and /dev/null differ diff --git a/pics/df48ea1b-3069-4fb7-93c0-4c8a26c7ed7c.png b/pics/df48ea1b-3069-4fb7-93c0-4c8a26c7ed7c.png deleted file mode 100644 index 4f0ffa3d..00000000 Binary files a/pics/df48ea1b-3069-4fb7-93c0-4c8a26c7ed7c.png and /dev/null differ diff --git a/pics/df648536-a107-48cd-a615-77b7a9b4025f.png b/pics/df648536-a107-48cd-a615-77b7a9b4025f.png deleted file mode 100644 index e709f110..00000000 Binary files a/pics/df648536-a107-48cd-a615-77b7a9b4025f.png and /dev/null differ diff --git a/pics/dfd078b2-aa4f-4c50-8319-232922d822b8.jpg b/pics/dfd078b2-aa4f-4c50-8319-232922d822b8.jpg deleted file mode 100644 index d708d036..00000000 Binary files a/pics/dfd078b2-aa4f-4c50-8319-232922d822b8.jpg and /dev/null differ diff --git a/pics/e024bd7e-fb4e-4239-9451-9a6227f50b00.jpg b/pics/e024bd7e-fb4e-4239-9451-9a6227f50b00.jpg deleted file mode 100644 index 99352484..00000000 Binary files a/pics/e024bd7e-fb4e-4239-9451-9a6227f50b00.jpg and /dev/null differ diff --git a/pics/e026c24d-00fa-4e7c-97a8-95a98cdc383a.png b/pics/e026c24d-00fa-4e7c-97a8-95a98cdc383a.png new file mode 100644 index 00000000..427cfbf8 Binary files /dev/null and b/pics/e026c24d-00fa-4e7c-97a8-95a98cdc383a.png differ diff --git a/pics/e0be6970-5b0e-44a2-bc71-df4d61c42b8f.jpg b/pics/e0be6970-5b0e-44a2-bc71-df4d61c42b8f.jpg deleted file mode 100644 index 0067761b..00000000 Binary files a/pics/e0be6970-5b0e-44a2-bc71-df4d61c42b8f.jpg and /dev/null differ diff --git a/pics/e13833c8-e215-462e-855c-1d362bb8d4a0.jpg b/pics/e13833c8-e215-462e-855c-1d362bb8d4a0.jpg deleted file mode 100644 index 3f535bf1..00000000 Binary files a/pics/e13833c8-e215-462e-855c-1d362bb8d4a0.jpg and /dev/null differ diff --git a/pics/e143f6da-d114-4ba4-8712-f65299047fa2.png b/pics/e143f6da-d114-4ba4-8712-f65299047fa2.png new file mode 100644 index 00000000..38cbf667 Binary files /dev/null and b/pics/e143f6da-d114-4ba4-8712-f65299047fa2.png differ diff --git a/pics/e198c201-f386-4491-8ad6-f7e433bf992d.png b/pics/e198c201-f386-4491-8ad6-f7e433bf992d.png deleted file mode 100644 index 5c0e1f15..00000000 Binary files a/pics/e198c201-f386-4491-8ad6-f7e433bf992d.png and /dev/null differ diff --git a/pics/e1cd89d1-8973-41d0-8ea9-940d94c314d9.jpg b/pics/e1cd89d1-8973-41d0-8ea9-940d94c314d9.jpg deleted file mode 100644 index c4fb4f11..00000000 Binary files a/pics/e1cd89d1-8973-41d0-8ea9-940d94c314d9.jpg and /dev/null differ diff --git a/pics/e2f0d889-2330-424c-8193-198edebecff7.png b/pics/e2f0d889-2330-424c-8193-198edebecff7.png new file mode 100644 index 00000000..d747d6f3 Binary files /dev/null and b/pics/e2f0d889-2330-424c-8193-198edebecff7.png differ diff --git a/pics/e3124763-f75e-46c3-ba82-341e6c98d862.jpg b/pics/e3124763-f75e-46c3-ba82-341e6c98d862.jpg new file mode 100644 index 00000000..80643657 Binary files /dev/null and b/pics/e3124763-f75e-46c3-ba82-341e6c98d862.jpg differ diff --git a/pics/e31abb94-9201-4e06-9902-61101b92f475.png b/pics/e31abb94-9201-4e06-9902-61101b92f475.png new file mode 100644 index 00000000..90833a5c Binary files /dev/null and b/pics/e31abb94-9201-4e06-9902-61101b92f475.png differ diff --git a/pics/e4ca3383-910a-4936-8ac2-9e8fd31b736b.png b/pics/e4ca3383-910a-4936-8ac2-9e8fd31b736b.png deleted file mode 100644 index 8ff86a82..00000000 Binary files a/pics/e4ca3383-910a-4936-8ac2-9e8fd31b736b.png and /dev/null differ diff --git a/pics/e5ad625e-729d-4a8d-923a-7c3df5773e1c.jpg b/pics/e5ad625e-729d-4a8d-923a-7c3df5773e1c.jpg deleted file mode 100644 index c232264b..00000000 Binary files a/pics/e5ad625e-729d-4a8d-923a-7c3df5773e1c.jpg and /dev/null differ diff --git a/pics/e5baeb38-0ec9-4ad7-8374-1cdb0dba74a6.jpg b/pics/e5baeb38-0ec9-4ad7-8374-1cdb0dba74a6.jpg deleted file mode 100644 index 917ae55a..00000000 Binary files a/pics/e5baeb38-0ec9-4ad7-8374-1cdb0dba74a6.jpg and /dev/null differ diff --git a/pics/e6723b94-1a33-4605-b775-f6813352d383.png b/pics/e6723b94-1a33-4605-b775-f6813352d383.png new file mode 100644 index 00000000..fdb9280e Binary files /dev/null and b/pics/e6723b94-1a33-4605-b775-f6813352d383.png differ diff --git a/pics/e7d7dc0d-fc22-4f95-8768-b8a216168792.jpg b/pics/e7d7dc0d-fc22-4f95-8768-b8a216168792.jpg deleted file mode 100644 index 5a7ea055..00000000 Binary files a/pics/e7d7dc0d-fc22-4f95-8768-b8a216168792.jpg and /dev/null differ diff --git a/pics/e800b001-7779-495b-8459-d33a7440d7b8.jpg b/pics/e800b001-7779-495b-8459-d33a7440d7b8.jpg new file mode 100644 index 00000000..07d6b7a9 Binary files /dev/null and b/pics/e800b001-7779-495b-8459-d33a7440d7b8.jpg differ diff --git a/pics/e84dd187-779f-4ffc-8ccc-40d8c03f5324.jpg b/pics/e84dd187-779f-4ffc-8ccc-40d8c03f5324.jpg deleted file mode 100644 index 76c44279..00000000 Binary files a/pics/e84dd187-779f-4ffc-8ccc-40d8c03f5324.jpg and /dev/null differ diff --git a/pics/e92d0ebc-7d46-413b-aec1-34a39602f787.png b/pics/e92d0ebc-7d46-413b-aec1-34a39602f787.png new file mode 100644 index 00000000..1090a779 Binary files /dev/null and b/pics/e92d0ebc-7d46-413b-aec1-34a39602f787.png differ diff --git a/pics/ea5e434a-a218-44b5-aa72-4cd08991abcf.jpg b/pics/ea5e434a-a218-44b5-aa72-4cd08991abcf.jpg deleted file mode 100644 index 386f0a6b..00000000 Binary files a/pics/ea5e434a-a218-44b5-aa72-4cd08991abcf.jpg and /dev/null differ diff --git a/pics/ea5ed9b2-6d9f-48fb-b890-0288caf9088a.jpg b/pics/ea5ed9b2-6d9f-48fb-b890-0288caf9088a.jpg deleted file mode 100644 index c69b6b27..00000000 Binary files a/pics/ea5ed9b2-6d9f-48fb-b890-0288caf9088a.jpg and /dev/null differ diff --git a/pics/ea5f3efe-d5e6-499b-b278-9e898af61257.jpg b/pics/ea5f3efe-d5e6-499b-b278-9e898af61257.jpg new file mode 100644 index 00000000..a07e736d Binary files /dev/null and b/pics/ea5f3efe-d5e6-499b-b278-9e898af61257.jpg differ diff --git a/pics/eb6271de-22c9-4f4b-8b31-eab1f560efac.png b/pics/eb6271de-22c9-4f4b-8b31-eab1f560efac.png deleted file mode 100644 index 05d5d201..00000000 Binary files a/pics/eb6271de-22c9-4f4b-8b31-eab1f560efac.png and /dev/null differ diff --git a/pics/ebc59de6-fa62-4118-92e5-7744b8ae893d.jpg b/pics/ebc59de6-fa62-4118-92e5-7744b8ae893d.jpg deleted file mode 100644 index 3bb64136..00000000 Binary files a/pics/ebc59de6-fa62-4118-92e5-7744b8ae893d.jpg and /dev/null differ diff --git a/pics/ebf03f56-f957-4435-9f8f-0f605661484d.jpg b/pics/ebf03f56-f957-4435-9f8f-0f605661484d.jpg deleted file mode 100644 index 90c92eac..00000000 Binary files a/pics/ebf03f56-f957-4435-9f8f-0f605661484d.jpg and /dev/null differ diff --git a/pics/ec2f0a65-82ad-4ab9-940f-70ee9f6992cc.png b/pics/ec2f0a65-82ad-4ab9-940f-70ee9f6992cc.png deleted file mode 100644 index 2c0df74f..00000000 Binary files a/pics/ec2f0a65-82ad-4ab9-940f-70ee9f6992cc.png and /dev/null differ diff --git a/pics/ed23a7cd-d55f-453d-b5bc-076d7b1c002e.jpg b/pics/ed23a7cd-d55f-453d-b5bc-076d7b1c002e.jpg deleted file mode 100644 index 30c81ddd..00000000 Binary files a/pics/ed23a7cd-d55f-453d-b5bc-076d7b1c002e.jpg and /dev/null differ diff --git a/pics/ed62f400-192c-4185-899b-187958201f0c.jpg b/pics/ed62f400-192c-4185-899b-187958201f0c.jpg deleted file mode 100644 index 3be32766..00000000 Binary files a/pics/ed62f400-192c-4185-899b-187958201f0c.jpg and /dev/null differ diff --git a/pics/edc23f99-c46c-4200-b64e-07516828720d.jpg b/pics/edc23f99-c46c-4200-b64e-07516828720d.jpg deleted file mode 100644 index 71abc4d3..00000000 Binary files a/pics/edc23f99-c46c-4200-b64e-07516828720d.jpg and /dev/null differ diff --git a/pics/ef280699-da36-4b38-9735-9b048a3c7fe0.png b/pics/ef280699-da36-4b38-9735-9b048a3c7fe0.png new file mode 100644 index 00000000..a54c85e1 Binary files /dev/null and b/pics/ef280699-da36-4b38-9735-9b048a3c7fe0.png differ diff --git a/pics/f0321ed1-fa93-460e-951b-4239fef819f3.jpg b/pics/f0321ed1-fa93-460e-951b-4239fef819f3.jpg deleted file mode 100644 index f9b99f27..00000000 Binary files a/pics/f0321ed1-fa93-460e-951b-4239fef819f3.jpg and /dev/null differ diff --git a/pics/f0e35b7a-2948-488a-a5a9-97d3f6b5e2d7.png b/pics/f0e35b7a-2948-488a-a5a9-97d3f6b5e2d7.png new file mode 100644 index 00000000..8f797bdf Binary files /dev/null and b/pics/f0e35b7a-2948-488a-a5a9-97d3f6b5e2d7.png differ diff --git a/pics/f1fb826b-ecf4-4ddb-91f0-2bafecf08869.jpg b/pics/f1fb826b-ecf4-4ddb-91f0-2bafecf08869.jpg deleted file mode 100644 index 736e72f4..00000000 Binary files a/pics/f1fb826b-ecf4-4ddb-91f0-2bafecf08869.jpg and /dev/null differ diff --git a/pics/f2af8957-d498-462f-9d11-f1c17876ba8e.png b/pics/f2af8957-d498-462f-9d11-f1c17876ba8e.png deleted file mode 100644 index 89c48683..00000000 Binary files a/pics/f2af8957-d498-462f-9d11-f1c17876ba8e.png and /dev/null differ diff --git a/pics/f2e0cee9-ecdc-4a96-853f-d9f6a1ad6ad1.jpg b/pics/f2e0cee9-ecdc-4a96-853f-d9f6a1ad6ad1.jpg deleted file mode 100644 index 7e71f69c..00000000 Binary files a/pics/f2e0cee9-ecdc-4a96-853f-d9f6a1ad6ad1.jpg and /dev/null differ diff --git a/pics/f3080f83-6239-459b-8e9c-03b6641f7815.png b/pics/f3080f83-6239-459b-8e9c-03b6641f7815.png new file mode 100644 index 00000000..77868686 Binary files /dev/null and b/pics/f3080f83-6239-459b-8e9c-03b6641f7815.png differ diff --git a/pics/f3bfe11f-9cba-4ff2-8cc6-629068408a80.jpg b/pics/f3bfe11f-9cba-4ff2-8cc6-629068408a80.jpg new file mode 100644 index 00000000..9dc7a1d5 Binary files /dev/null and b/pics/f3bfe11f-9cba-4ff2-8cc6-629068408a80.jpg differ diff --git a/pics/f42443e0-208d-41ea-be44-c7fd97d2e3bf.png b/pics/f42443e0-208d-41ea-be44-c7fd97d2e3bf.png new file mode 100644 index 00000000..4a5dbd1d Binary files /dev/null and b/pics/f42443e0-208d-41ea-be44-c7fd97d2e3bf.png differ diff --git a/pics/f48e2b92-2c2a-48cb-a443-bd313e187a25.jpg b/pics/f48e2b92-2c2a-48cb-a443-bd313e187a25.jpg new file mode 100644 index 00000000..66c29824 Binary files /dev/null and b/pics/f48e2b92-2c2a-48cb-a443-bd313e187a25.jpg differ diff --git a/pics/f50a8e52-a683-444c-8e32-63c1890fe84a.jpg b/pics/f50a8e52-a683-444c-8e32-63c1890fe84a.jpg new file mode 100644 index 00000000..46a01b3e Binary files /dev/null and b/pics/f50a8e52-a683-444c-8e32-63c1890fe84a.jpg differ diff --git a/pics/f50bc364-fdc2-4a46-9b8f-f8f5b6add3b8.jpg b/pics/f50bc364-fdc2-4a46-9b8f-f8f5b6add3b8.jpg deleted file mode 100644 index ec059ca1..00000000 Binary files a/pics/f50bc364-fdc2-4a46-9b8f-f8f5b6add3b8.jpg and /dev/null differ diff --git a/pics/f5477abd-c246-4851-89ab-6b1cde2549b1.png b/pics/f5477abd-c246-4851-89ab-6b1cde2549b1.png new file mode 100644 index 00000000..f358a009 Binary files /dev/null and b/pics/f5477abd-c246-4851-89ab-6b1cde2549b1.png differ diff --git a/pics/f60c2116-fd19-4431-a57c-102fcc41ebd9.jpg b/pics/f60c2116-fd19-4431-a57c-102fcc41ebd9.jpg deleted file mode 100644 index 8fc059bf..00000000 Binary files a/pics/f60c2116-fd19-4431-a57c-102fcc41ebd9.jpg and /dev/null differ diff --git a/pics/f61b5419-c94a-4df1-8d4d-aed9ae8cc6d5.png b/pics/f61b5419-c94a-4df1-8d4d-aed9ae8cc6d5.png new file mode 100644 index 00000000..dc0d4e34 Binary files /dev/null and b/pics/f61b5419-c94a-4df1-8d4d-aed9ae8cc6d5.png differ diff --git a/pics/f6be22cb-d64f-4ee5-87b7-cbc4e6255c0e.jpg b/pics/f6be22cb-d64f-4ee5-87b7-cbc4e6255c0e.jpg deleted file mode 100644 index bb25340d..00000000 Binary files a/pics/f6be22cb-d64f-4ee5-87b7-cbc4e6255c0e.jpg and /dev/null differ diff --git a/pics/f716427a-94f2-4875-9c86-98793cf5dcc3.jpg b/pics/f716427a-94f2-4875-9c86-98793cf5dcc3.jpg new file mode 100644 index 00000000..15da1c6b Binary files /dev/null and b/pics/f716427a-94f2-4875-9c86-98793cf5dcc3.jpg differ diff --git a/pics/f7d5da89-2d75-4d8f-85e7-6b608865dc00.jpg b/pics/f7d5da89-2d75-4d8f-85e7-6b608865dc00.jpg deleted file mode 100644 index a0abac53..00000000 Binary files a/pics/f7d5da89-2d75-4d8f-85e7-6b608865dc00.jpg and /dev/null differ diff --git a/pics/f7d880c9-740a-4a16-ac6d-be502281b4b2.jpg b/pics/f7d880c9-740a-4a16-ac6d-be502281b4b2.jpg deleted file mode 100644 index d1232fcf..00000000 Binary files a/pics/f7d880c9-740a-4a16-ac6d-be502281b4b2.jpg and /dev/null differ diff --git a/pics/f87afe72-c2df-4c12-ac03-9b8d581a8af8.jpg b/pics/f87afe72-c2df-4c12-ac03-9b8d581a8af8.jpg new file mode 100644 index 00000000..6a090993 Binary files /dev/null and b/pics/f87afe72-c2df-4c12-ac03-9b8d581a8af8.jpg differ diff --git a/pics/f8b12555-967b-423d-a84e-bc9eff104b8b.jpg b/pics/f8b12555-967b-423d-a84e-bc9eff104b8b.jpg deleted file mode 100644 index 9e26464a..00000000 Binary files a/pics/f8b12555-967b-423d-a84e-bc9eff104b8b.jpg and /dev/null differ diff --git a/pics/f8b16d1e-7363-4544-94d6-4939fdf849dc.png b/pics/f8b16d1e-7363-4544-94d6-4939fdf849dc.png new file mode 100644 index 00000000..300b6e06 Binary files /dev/null and b/pics/f8b16d1e-7363-4544-94d6-4939fdf849dc.png differ diff --git a/pics/f99c019e-7e91-4c2e-b94d-b031c402dcb5.jpg b/pics/f99c019e-7e91-4c2e-b94d-b031c402dcb5.jpg deleted file mode 100644 index af9fe3e8..00000000 Binary files a/pics/f99c019e-7e91-4c2e-b94d-b031c402dcb5.jpg and /dev/null differ diff --git a/pics/f9ed4da5-0032-41e6-991a-36d995ec28fd.png b/pics/f9ed4da5-0032-41e6-991a-36d995ec28fd.png deleted file mode 100644 index cf85f6be..00000000 Binary files a/pics/f9ed4da5-0032-41e6-991a-36d995ec28fd.png and /dev/null differ diff --git a/pics/f9f9f993-8ece-4da7-b848-af9b438fad76.png b/pics/f9f9f993-8ece-4da7-b848-af9b438fad76.png new file mode 100644 index 00000000..824904db Binary files /dev/null and b/pics/f9f9f993-8ece-4da7-b848-af9b438fad76.png differ diff --git a/pics/fa4101d7-19ce-4a69-a84f-20bbe64320e5.jpg b/pics/fa4101d7-19ce-4a69-a84f-20bbe64320e5.jpg deleted file mode 100644 index 0d9e2948..00000000 Binary files a/pics/fa4101d7-19ce-4a69-a84f-20bbe64320e5.jpg and /dev/null differ diff --git a/pics/fabd5fa0-b75e-48d0-9e2c-31471945ceb9.jpg b/pics/fabd5fa0-b75e-48d0-9e2c-31471945ceb9.jpg deleted file mode 100644 index 650394df..00000000 Binary files a/pics/fabd5fa0-b75e-48d0-9e2c-31471945ceb9.jpg and /dev/null differ diff --git a/pics/fd20f4f9-e5d8-43cf-bf4f-2057c18d01fb.png b/pics/fd20f4f9-e5d8-43cf-bf4f-2057c18d01fb.png deleted file mode 100644 index 1bb5f234..00000000 Binary files a/pics/fd20f4f9-e5d8-43cf-bf4f-2057c18d01fb.png and /dev/null differ diff --git a/pics/fe3d224c-8ffd-40f9-85b1-86ffe1393f6c.jpg b/pics/fe3d224c-8ffd-40f9-85b1-86ffe1393f6c.jpg deleted file mode 100644 index 6cd4d187..00000000 Binary files a/pics/fe3d224c-8ffd-40f9-85b1-86ffe1393f6c.jpg and /dev/null differ diff --git a/pics/ff0c019c-6461-467d-a266-0455341fd4f4.png b/pics/ff0c019c-6461-467d-a266-0455341fd4f4.png new file mode 100644 index 00000000..658867d2 Binary files /dev/null and b/pics/ff0c019c-6461-467d-a266-0455341fd4f4.png differ diff --git a/pics/ff17c103-750a-4bb8-9afa-576327023af9.png b/pics/ff17c103-750a-4bb8-9afa-576327023af9.png deleted file mode 100644 index 5f9f99b3..00000000 Binary files a/pics/ff17c103-750a-4bb8-9afa-576327023af9.png and /dev/null differ diff --git a/pics/ff396233-1bb1-4e74-8bc2-d7c90146f5dd.png b/pics/ff396233-1bb1-4e74-8bc2-d7c90146f5dd.png new file mode 100644 index 00000000..4eaa11ba Binary files /dev/null and b/pics/ff396233-1bb1-4e74-8bc2-d7c90146f5dd.png differ diff --git a/pics/monitor-lock-rule.png b/pics/monitor-lock-rule.png new file mode 100644 index 00000000..6590d94b Binary files /dev/null and b/pics/monitor-lock-rule.png differ diff --git a/pics/mutualssl_small.png b/pics/mutualssl_small.png new file mode 100644 index 00000000..1541eba8 Binary files /dev/null and b/pics/mutualssl_small.png differ diff --git a/pics/n2U3N.png b/pics/n2U3N.png index 88b27864..cd58b5b9 100644 Binary files a/pics/n2U3N.png and b/pics/n2U3N.png differ diff --git a/pics/pcrypt.gif b/pics/pcrypt.gif new file mode 100644 index 00000000..c17b9853 Binary files /dev/null and b/pics/pcrypt.gif differ diff --git a/pics/scrypt.gif b/pics/scrypt.gif new file mode 100644 index 00000000..150fea7f Binary files /dev/null and b/pics/scrypt.gif differ diff --git a/pics/single-thread-rule.png b/pics/single-thread-rule.png new file mode 100644 index 00000000..d6583e9e Binary files /dev/null and b/pics/single-thread-rule.png differ diff --git a/pics/ssl-offloading.jpg b/pics/ssl-offloading.jpg new file mode 100644 index 00000000..8f01a418 Binary files /dev/null and b/pics/ssl-offloading.jpg differ diff --git a/pics/tGPV0.png b/pics/tGPV0.png index ee6a3c08..89fb7bfe 100644 Binary files a/pics/tGPV0.png and b/pics/tGPV0.png differ diff --git a/pics/thread-join-rule.png b/pics/thread-join-rule.png new file mode 100644 index 00000000..c17d0456 Binary files /dev/null and b/pics/thread-join-rule.png differ diff --git a/pics/thread-start-rule.png b/pics/thread-start-rule.png new file mode 100644 index 00000000..60ee7862 Binary files /dev/null and b/pics/thread-start-rule.png differ diff --git a/pics/url_diagram.png b/pics/url_diagram.png new file mode 100644 index 00000000..18bbd49b Binary files /dev/null and b/pics/url_diagram.png differ diff --git a/pics/volatile-variable-rule.png b/pics/volatile-variable-rule.png new file mode 100644 index 00000000..1747664b Binary files /dev/null and b/pics/volatile-variable-rule.png differ diff --git a/pics/xss-attack.png b/pics/xss-attack.png deleted file mode 100644 index a3025d4d..00000000 Binary files a/pics/xss-attack.png and /dev/null differ