From 84f0ffcdd849edac070807b86027777593846125 Mon Sep 17 00:00:00 2001 From: ZiHang Gao Date: Thu, 12 Jul 2018 12:08:43 +0800 Subject: [PATCH 01/26] Fix typ (eopll -> epoll) --- notes/Socket.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/notes/Socket.md b/notes/Socket.md index 13f1eaf5..99073da3 100644 --- a/notes/Socket.md +++ b/notes/Socket.md @@ -12,7 +12,7 @@ * [poll](#poll) * [epoll](#epoll) * [select 和 poll 比较](#select-和-poll-比较) - * [eopll 工作模式](#eopll-工作模式) + * [epoll 工作模式](#epoll-工作模式) * [应用场景](#应用场景) * [参考资料](#参考资料) @@ -303,7 +303,7 @@ poll 和 select 在速度上都很慢。 几乎所有的系统都支持 select,但是只有比较新的系统支持 poll。 -## eopll 工作模式 +## epoll 工作模式 epoll_event 有两种触发模式:LT(level trigger)和 ET(edge trigger)。 From 906e58ef7cad4f0fcc08c41e7223c942679ce69d Mon Sep 17 00:00:00 2001 From: xiangflight Date: Thu, 12 Jul 2018 14:51:39 +0800 Subject: [PATCH 02/26] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=BA=86=E9=83=A8?= =?UTF-8?q?=E5=88=86CopyOnWriteArrayList=E7=9A=84=E7=AE=80=E4=BB=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .DS_Store | Bin 0 -> 6148 bytes notes/Java 容器.md | 25 +++++++++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..b36190334234c2eda81663fa9356d3044da733a1 GIT binary patch literal 6148 zcmeHK&1(}u6n|5r?lyjGYpbm&EaqUrB9&G>h`7d9Eh?!=tc}&iO?H|tZfC;ornQzr z5WEUr6~Tiac=8VrJg5i%1TW&BpcfCG^vy?TO!X=v`@+oc<<0NC-T5uEvjYGm+whJ6 zSO8#P6PW78?l&gJMcZIqIwA&%tZ@-0p#ViV42v7sNgz=mQQ%)wK&;&~WFd#Y3(&rP zy~%)AD*>+{L%beNU>o`&4LyL(z<@&x6rv91%Re(K%TZm|{8p(4eiV<4d?a0)Qk%DI zHO%g`nck5$Zxj~zav^r(MwE5KvbC?^d$i()SA8#DC{i{N&?U-p;YBy5;jADRIS-Ja ze77>~)8(?&cZRdr^%)IiJyqlSRiAl$Im;W&izHtcm;RI+FJK)b$qOtqt3&$B&N;+he20*49kJ7(943Ki9Z>|KX#j&z`?{`%Y#7!PlX*9*r${ zk4~F#esxL9LEg0F_-t8if^6HqW2e>AyQ`=TMx)`=R!)Ete2agk7zto zwsuC;EoVqM7G#tIka1QV=^GX)GR~-J zW}GQKYQP~l4VPdVZowUR0FU7nyoL|(3BJI0vXu;y1LP1HBPYoza-Li!c~T@La*fQY z{I1q>-LLCuARanx&vmH~vVgN1Mny&n>fl4QbD@wuIM%+TOvu9o-rqRh;e^~oI~F0i z*OAeU+wB!(U&5?Blzx{Z7w+@6g^MMW>B$)BRv^Jdfkc5s0j+>|KiJp=IvSe_<*Nfb zc?3XAVOSda;$uPN*cu&;O@%muMpP=IN+tS;K~y@%ZOwBuHWjLLAo}n@^p%M|p%8g> ztZz#?5JzFsCkiAA{Hnkv_16;n|HPlq|Gy?B{}Kff1^z1qNNT(=p2aEYy>-Ln#9nJ- uyNOMh;58M>6zueNEFG~G4`7pqW43S*9gR(e7(q)u1f&c}mniU875E7(>Kowz literal 0 HcmV?d00001 diff --git a/notes/Java 容器.md b/notes/Java 容器.md index 55cffe2c..bdb97bb5 100644 --- a/notes/Java 容器.md +++ b/notes/Java 容器.md @@ -255,6 +255,31 @@ List synList = Collections.synchronizedList(list); List list = new CopyOnWriteArrayList<>(); ``` +扩展:CopyOnWriteArrayList是一种CopyOnWrite容器,即写时复制的容器。从以下源码中看出:当向容器中添加元素时,不是直接往当前容器添加,而是Copy当前容器,在新容器中添加,添加完后,将原容器的引用指向新容器。这样做可以对CopyOnWrite容器进行并发的读,不需要加锁,极大地提高了读性能,因此适用于读多写少的场景。 + +```java +public boolean add(E e) { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + Object[] elements = getArray(); + int len = elements.length; + Object[] newElements = Arrays.copyOf(elements, len + 1); // 复制到新容器 + newElements[len] = e; + setArray(newElements); + return true; + } finally { + lock.unlock(); + } +} + +@SuppressWarnings("unchecked") +private E get(Object[] a, int index) { + return (E) a[index]; +} + +``` + ## LinkedList ### 1. 概览 From 08457794f7771a6417991d614975d26a378a9f5a Mon Sep 17 00:00:00 2001 From: xiangflight Date: Thu, 12 Jul 2018 14:54:44 +0800 Subject: [PATCH 03/26] =?UTF-8?q?=E6=B7=BB=E5=8A=A0DS=5Fstore=E5=88=B0git.?= =?UTF-8?q?ignore=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .DS_Store | Bin 6148 -> 0 bytes .gitignore | 1 + 2 files changed, 1 insertion(+) delete mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index b36190334234c2eda81663fa9356d3044da733a1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHK&1(}u6n|5r?lyjGYpbm&EaqUrB9&G>h`7d9Eh?!=tc}&iO?H|tZfC;ornQzr z5WEUr6~Tiac=8VrJg5i%1TW&BpcfCG^vy?TO!X=v`@+oc<<0NC-T5uEvjYGm+whJ6 zSO8#P6PW78?l&gJMcZIqIwA&%tZ@-0p#ViV42v7sNgz=mQQ%)wK&;&~WFd#Y3(&rP zy~%)AD*>+{L%beNU>o`&4LyL(z<@&x6rv91%Re(K%TZm|{8p(4eiV<4d?a0)Qk%DI zHO%g`nck5$Zxj~zav^r(MwE5KvbC?^d$i()SA8#DC{i{N&?U-p;YBy5;jADRIS-Ja ze77>~)8(?&cZRdr^%)IiJyqlSRiAl$Im;W&izHtcm;RI+FJK)b$qOtqt3&$B&N;+he20*49kJ7(943Ki9Z>|KX#j&z`?{`%Y#7!PlX*9*r${ zk4~F#esxL9LEg0F_-t8if^6HqW2e>AyQ`=TMx)`=R!)Ete2agk7zto zwsuC;EoVqM7G#tIka1QV=^GX)GR~-J zW}GQKYQP~l4VPdVZowUR0FU7nyoL|(3BJI0vXu;y1LP1HBPYoza-Li!c~T@La*fQY z{I1q>-LLCuARanx&vmH~vVgN1Mny&n>fl4QbD@wuIM%+TOvu9o-rqRh;e^~oI~F0i z*OAeU+wB!(U&5?Blzx{Z7w+@6g^MMW>B$)BRv^Jdfkc5s0j+>|KiJp=IvSe_<*Nfb zc?3XAVOSda;$uPN*cu&;O@%muMpP=IN+tS;K~y@%ZOwBuHWjLLAo}n@^p%M|p%8g> ztZz#?5JzFsCkiAA{Hnkv_16;n|HPlq|Gy?B{}Kff1^z1qNNT(=p2aEYy>-Ln#9nJ- uyNOMh;58M>6zueNEFG~G4`7pqW43S*9gR(e7(q)u1f&c}mniU875E7(>Kowz diff --git a/.gitignore b/.gitignore index e69de29b..e43b0f98 100644 --- a/.gitignore +++ b/.gitignore @@ -0,0 +1 @@ +.DS_Store From e7d694d841049c93dc9dce27e6846231a56fb32f Mon Sep 17 00:00:00 2001 From: xiangflight Date: Thu, 12 Jul 2018 15:15:06 +0800 Subject: [PATCH 04/26] =?UTF-8?q?=E4=BF=AE=E6=94=B9CopyOnWrite=E9=83=A8?= =?UTF-8?q?=E5=88=86=E7=9A=84=E8=A1=A8=E8=BF=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- notes/Java 容器.md | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/notes/Java 容器.md b/notes/Java 容器.md index bdb97bb5..cc4e93ad 100644 --- a/notes/Java 容器.md +++ b/notes/Java 容器.md @@ -255,17 +255,18 @@ List synList = Collections.synchronizedList(list); List list = new CopyOnWriteArrayList<>(); ``` -扩展:CopyOnWriteArrayList是一种CopyOnWrite容器,即写时复制的容器。从以下源码中看出:当向容器中添加元素时,不是直接往当前容器添加,而是Copy当前容器,在新容器中添加,添加完后,将原容器的引用指向新容器。这样做可以对CopyOnWrite容器进行并发的读,不需要加锁,极大地提高了读性能,因此适用于读多写少的场景。 +扩展 +CopyOnWriteArrayList是一种CopyOnWrite容器,从以下源码看出:添加元素是在复制的新数组上进行的,然后将原数组的引用指向新数组;读取元素是从原数组读取。这样可以进行并发的且不需加锁的读取,读取效率高,适用于读操作远大于写操作的场景中。 ```java public boolean add(E e) { - final ReentrantLock lock = this.lock; + final ReentrantLock lock = this.lock; lock.lock(); try { Object[] elements = getArray(); int len = elements.length; - Object[] newElements = Arrays.copyOf(elements, len + 1); // 复制到新容器 - newElements[len] = e; + Object[] newElements = Arrays.copyOf(elements, len + 1); + newElements[len] = e; setArray(newElements); return true; } finally { @@ -273,9 +274,13 @@ public boolean add(E e) { } } +final void setArray(Object[] a) { + array = a; +} + @SuppressWarnings("unchecked") private E get(Object[] a, int index) { - return (E) a[index]; + return (E) a[index]; } ``` From 3fd6fee78871cb9e0a867dfa0e31557900df5c17 Mon Sep 17 00:00:00 2001 From: Kdashnight <274951525@qq.com> Date: Thu, 12 Jul 2018 15:31:42 +0800 Subject: [PATCH 05/26] =?UTF-8?q?Update=20=E6=95=B0=E6=8D=AE=E5=BA=93?= =?UTF-8?q?=E7=B3=BB=E7=BB=9F=E5=8E=9F=E7=90=86.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- notes/数据库系统原理.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notes/数据库系统原理.md b/notes/数据库系统原理.md index 4aaa9044..d7e3d8d6 100644 --- a/notes/数据库系统原理.md +++ b/notes/数据库系统原理.md @@ -25,7 +25,7 @@ * [快照读与当前读](#快照读与当前读) * [六、Next-Key Locks](#六next-key-locks) * [Record Locks](#record-locks) - * [Grap Locks](#grap-locks) + * [Gap Locks](#gap-locks) * [Next-Key Locks](#next-key-locks) * [七、关系数据库设计理论](#七关系数据库设计理论) * [函数依赖](#函数依赖) From 1e26cd1af42cead6aa6e6494158ef0373920533c Mon Sep 17 00:00:00 2001 From: xiangflight Date: Thu, 12 Jul 2018 19:45:30 +0800 Subject: [PATCH 06/26] =?UTF-8?q?=E7=AE=80=E5=8C=96=E5=AF=B9CopyOnWrite?= =?UTF-8?q?=E7=9A=84=E6=8F=8F=E8=BF=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- notes/Java 容器.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/notes/Java 容器.md b/notes/Java 容器.md index cc4e93ad..f5e7ee10 100644 --- a/notes/Java 容器.md +++ b/notes/Java 容器.md @@ -255,8 +255,7 @@ List synList = Collections.synchronizedList(list); List list = new CopyOnWriteArrayList<>(); ``` -扩展 -CopyOnWriteArrayList是一种CopyOnWrite容器,从以下源码看出:添加元素是在复制的新数组上进行的,然后将原数组的引用指向新数组;读取元素是从原数组读取。这样可以进行并发的且不需加锁的读取,读取效率高,适用于读操作远大于写操作的场景中。 +CopyOnWriteArrayList是一种CopyOnWrite容器,从以下源码看出:读取元素是从原数组读取;添加元素是在复制的新数组上。读写分离,因而可以在并发条件下进行不加锁的读取,读取效率高,适用于读操作远大于写操作的场景。 ```java public boolean add(E e) { From c18744d669308f7a4989159013ca58666bee0711 Mon Sep 17 00:00:00 2001 From: Gsllchb Date: Sat, 14 Jul 2018 11:36:20 +0800 Subject: [PATCH 07/26] fix typo --- notes/MySQL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notes/MySQL.md b/notes/MySQL.md index e460d98a..5348018d 100644 --- a/notes/MySQL.md +++ b/notes/MySQL.md @@ -250,7 +250,7 @@ SELECT actor_id FROM sakila.actor WHERE actor_id + 1 = 5; ```sql SELECT film_id, actor_ id FROM sakila.film_actor -WhERE actor_id = 1 AND film_id = 1; +WHERE actor_id = 1 AND film_id = 1; ``` ### 3. 索引列的顺序 From 88b4fc87ef3cc411f0d3cd5a0a6febc3114c8c37 Mon Sep 17 00:00:00 2001 From: Gsllchb Date: Sat, 14 Jul 2018 15:45:43 +0800 Subject: [PATCH 08/26] fix typo --- notes/Java 基础.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notes/Java 基础.md b/notes/Java 基础.md index 842ebd84..e5671d34 100644 --- a/notes/Java 基础.md +++ b/notes/Java 基础.md @@ -1112,7 +1112,7 @@ Reflection is powerful, but should not be used indiscriminately. If it is possib Throwable 可以用来表示任何可以作为异常抛出的类,分为两种: **Error** 和 **Exception**。其中 Error 用来表示 JVM 无法处理的错误,Exception 分为两种: - **受检异常** :需要用 try...catch... 语句捕获并进行处理,并且可以从异常中恢复; -- **非受检异常** :是程序运行时错误,例如除 0 会引发 Arithmetic Exception,此时程序奔溃并且无法恢复。 +- **非受检异常** :是程序运行时错误,例如除 0 会引发 Arithmetic Exception,此时程序崩溃并且无法恢复。

From 033676724523021872edb86176e92a87b87acd46 Mon Sep 17 00:00:00 2001 From: CyC2018 <1029579233@qq.com> Date: Sun, 15 Jul 2018 21:57:21 +0800 Subject: [PATCH 09/26] auto commit --- notes/Java IO.md | 62 +++++++++++++++++++--------------- notes/Java 基础.md | 2 +- notes/Java 容器.md | 7 ++-- notes/SQL.md | 1 - notes/Socket.md | 4 +-- notes/数据库系统原理.md | 2 +- 6 files changed, 42 insertions(+), 36 deletions(-) diff --git a/notes/Java IO.md b/notes/Java IO.md index c41f7f05..08dc80f4 100644 --- a/notes/Java IO.md +++ b/notes/Java IO.md @@ -49,7 +49,7 @@ public static void listAllFiles(File dir) System.out.println(dir.getName()); return; } - for (File file : Objects.requireNonNull(dir.listFiles())) { + for (File file : dir.listFiles()) { listAllFiles(file); } } @@ -65,8 +65,9 @@ public static void copyFile(String src, String dist) throws IOException FileInputStream in = new FileInputStream(src); FileOutputStream out = new FileOutputStream(dist); byte[] buffer = new byte[20 * 1024]; - /* read() 最多读取 buffer.length 个字节 - 返回的是实际读取的个数,返回 -1 的时候表示读到 eof,即文件尾 */ + // read() 最多读取 buffer.length 个字节 + // 返回的是实际读取的个数 + // 返回 -1 的时候表示读到 eof,即文件尾 while (in.read(buffer, 0, buffer.length) != -1) { out.write(buffer); } @@ -82,7 +83,7 @@ Java I/O 使用了装饰者模式来实现。以 InputStream 为例,InputStrea 实例化一个具有缓存功能的字节流对象时,只需要在 FileInputStream 对象上再套一层 BufferedInputStream 对象即可。 ```java -FileInputStream fileInputStream = new FileInputStream("file/1.txt"); +FileInputStream fileInputStream = new FileInputStream(filePath); BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream); ``` @@ -92,22 +93,25 @@ DataInputStream 装饰者提供了对更多数据类型进行输入的操作, 不管是磁盘还是网络传输,最小的存储单元都是字节,而不是字符。但是在程序中操作的通常是字符形式的数据,因此需要提供对字符进行操作的方法。 -- InputStreamReader 实现从文本文件的字节流解码成字符流; -- OutputStreamWriter 实现字符流编码成为文本文件的字节流。 +- InputStreamReader 实现从字节流解码成字符流; +- OutputStreamWriter 实现字符流编码成为字节流。 逐行输出文本文件的内容: ```java -FileReader fileReader = new FileReader("file/1.txt"); -BufferedReader bufferedReader = new BufferedReader(fileReader); -String line; -while ((line = bufferedReader.readLine()) != null) { - System.out.println(line); +public static void readFileContent(String filePath) throws IOException +{ + FileReader fileReader = new FileReader(filePath); + BufferedReader bufferedReader = new BufferedReader(fileReader); + String line; + while ((line = bufferedReader.readLine()) != null) { + System.out.println(line); + } + // 装饰者模式使得 BufferedReader 组合了一个 Reader 对象 + // 在调用 BufferedReader 的 close() 方法时会去调用 fileReader 的 close() 方法 + // 因此只要一个 close() 调用即可 + bufferedReader.close(); } -/* 装饰者模式使得 BufferedReader 组合了一个 Reader 对象 - 在调用 BufferedReader 的 close() 方法时会去调用 fileReader 的 close() 方法 - 因此只要一个 close() 调用即可 */ -bufferedReader.close(); ``` 编码就是把字符转换为字节,而解码是把字节重新组合成字符。 @@ -216,8 +220,10 @@ InetAddress.getByAddress(byte[] address); public static void main(String[] args) throws IOException { URL url = new URL("http://www.baidu.com"); - InputStream is = url.openStream(); /* 字节流 */ - InputStreamReader isr = new InputStreamReader(is, "utf-8"); /* 字符流 */ + // 字节流 + InputStream is = url.openStream(); + // 字符流 + InputStreamReader isr = new InputStreamReader(is, "utf-8"); BufferedReader br = new BufferedReader(isr); String line = br.readLine(); while (line != null) { @@ -253,7 +259,7 @@ public static void main(String[] args) throws IOException I/O 与 NIO 最重要的区别是数据打包和传输的方式,I/O 以流的方式处理数据,而 NIO 以块的方式处理数据。 -面向流的 I/O 一次处理一个字节数据,一个输入流产生一个字节数据,一个输出流消费一个字节数据。为流式数据创建过滤器非常容易,链接几个过滤器,以便每个过滤器只负责复杂处理机制的一部分。不利的一面是,面向流的 I/O 通常相当慢。 +面向流的 I/O 一次处理一个字节数据:一个输入流产生一个字节数据,一个输出流消费一个字节数据。为流式数据创建过滤器非常容易,链接几个过滤器,以便每个过滤器只负责复杂处理机制的一部分。不利的一面是,面向流的 I/O 通常相当慢。 面向块的 I/O 一次处理一个数据块,按块处理数据比按流处理数据要快得多。但是面向块的 I/O 缺少一些面向流的 I/O 所具有的优雅性和简单性。 @@ -325,7 +331,7 @@ I/O 包和 NIO 已经很好地集成了,java.io.\* 已经以 NIO 为基础重 ```java public static void fastCopy(String src, String dist) throws IOException { - FileInputStream fin = new FileInputStream(src); /* 获得源文件的输入字节流 */ + FileInputStream fin = new FileInputStream(src); /* 获取源文件的输入字节流 */ FileChannel fcin = fin.getChannel(); /* 获取输入字节流的文件通道 */ FileOutputStream fout = new FileOutputStream(dist); /* 获取目标文件的输出字节流 */ FileChannel fcout = fout.getChannel(); /* 获取输出字节流的通道 */ @@ -344,10 +350,16 @@ public static void fastCopy(String src, String dist) throws IOException ## 选择器 -一个线程 Thread 使用一个选择器 Selector 通过轮询的方式去监听多个通道 Channel 上的事件,从而让一个线程就可以处理多个事件。 +NIO 常常被叫做非阻塞 IO,主要是因为 NIO 在网络通信中的非阻塞特性被广泛使用。 + +NIO 实现了 IO 多路复用中的 Reactor 模型,一个线程 Thread 使用一个选择器 Selector 通过轮询的方式去监听多个通道 Channel 上的事件,从而让一个线程就可以处理多个事件。 + +通过配置监听的通道 Channel 为非阻塞,那么当 Channel 上的 IO 事件还未到达时,就不会进入阻塞状态一直等待,而是继续轮询其它 Channel,找到 IO 事件已经到达的 Channel 执行。 因为创建和切换线程的开销很大,因此使用一个线程来处理多个事件而不是一个线程处理一个事件具有更好的性能。 +应该注意的是,只有套接字 Channel 才能配置为非阻塞,而 FileChannel 不能,为 FileChannel 配置非阻塞也没有意义。 +

### 1. 创建选择器 @@ -394,7 +406,7 @@ int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE; int num = selector.select(); ``` -使用 select() 来监听事件到达,它会一直阻塞直到有至少一个事件到达。 +使用 select() 来监听到达的事件,它会一直阻塞直到有至少一个事件到达。 ### 4. 获取到达的事件 @@ -514,10 +526,6 @@ public class NIOClient 内存映射文件 I/O 是一种读和写文件数据的方法,它可以比常规的基于流或者基于通道的 I/O 快得多。 -只有文件中实际读取或者写入的部分才会映射到内存中。 - -现代操作系统一般会根据需要将文件的部分映射为内存的部分,从而实现文件系统。Java 内存映射机制只不过是在底层操作系统中可以采用这种机制时,提供了对该机制的访问。 - 向内存映射文件写入可能是危险的,仅只是改变数组的单个元素这样的简单操作,就可能会直接修改磁盘上的文件。修改数据与将数据保存到磁盘是没有分开的。 下面代码行将文件的前 1024 个字节映射到内存中,map() 方法返回一个 MappedByteBuffer,它是 ByteBuffer 的子类。因此,您可以像使用其他任何 ByteBuffer 一样使用新映射的缓冲区,操作系统会在需要时负责执行映射。 @@ -530,8 +538,8 @@ MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_WRITE, 0, 1024); NIO 与普通 I/O 的区别主要有以下两点: -- NIO 是非阻塞的。应当注意,FileChannel 不能切换到非阻塞模式,套接字 Channel 可以。 -- NIO 面向块,I/O 面向流。 +- NIO 是非阻塞的 +- NIO 面向块,I/O 面向流 # 八、参考资料 diff --git a/notes/Java 基础.md b/notes/Java 基础.md index e5671d34..af1c6530 100644 --- a/notes/Java 基础.md +++ b/notes/Java 基础.md @@ -484,7 +484,7 @@ System.out.println(InterfaceExample.x); 使用抽象类: - 需要在几个相关的类中共享代码。 -- 需要能控制继承来的方法和域的访问权限,而不是都为 public。 +- 需要能控制继承来的成员的访问权限,而不是都为 public。 - 需要继承非静态(non-static)和非常量(non-final)字段。 使用接口: diff --git a/notes/Java 容器.md b/notes/Java 容器.md index f5e7ee10..9e6e2ac3 100644 --- a/notes/Java 容器.md +++ b/notes/Java 容器.md @@ -255,7 +255,7 @@ List synList = Collections.synchronizedList(list); List list = new CopyOnWriteArrayList<>(); ``` -CopyOnWriteArrayList是一种CopyOnWrite容器,从以下源码看出:读取元素是从原数组读取;添加元素是在复制的新数组上。读写分离,因而可以在并发条件下进行不加锁的读取,读取效率高,适用于读操作远大于写操作的场景。 +CopyOnWriteArrayList 是一种 CopyOnWrite 容器,从以下源码看出:读取元素是从原数组读取;添加元素是在复制的新数组上。读写分离,因而可以在并发条件下进行不加锁的读取,读取效率高,适用于读操作远大于写操作的场景。 ```java public boolean add(E e) { @@ -265,7 +265,7 @@ public boolean add(E e) { Object[] elements = getArray(); int len = elements.length; Object[] newElements = Arrays.copyOf(elements, len + 1); - newElements[len] = e; + newElements[len] = e; setArray(newElements); return true; } finally { @@ -279,9 +279,8 @@ final void setArray(Object[] a) { @SuppressWarnings("unchecked") private E get(Object[] a, int index) { - return (E) a[index]; + return (E) a[index]; } - ``` ## LinkedList diff --git a/notes/SQL.md b/notes/SQL.md index 3bbb121f..ca9b7b29 100644 --- a/notes/SQL.md +++ b/notes/SQL.md @@ -170,7 +170,6 @@ FROM mytable LIMIT 2, 3; ``` - # 八、排序 - **ASC** :升序(默认) diff --git a/notes/Socket.md b/notes/Socket.md index 99073da3..13f1eaf5 100644 --- a/notes/Socket.md +++ b/notes/Socket.md @@ -12,7 +12,7 @@ * [poll](#poll) * [epoll](#epoll) * [select 和 poll 比较](#select-和-poll-比较) - * [epoll 工作模式](#epoll-工作模式) + * [eopll 工作模式](#eopll-工作模式) * [应用场景](#应用场景) * [参考资料](#参考资料) @@ -303,7 +303,7 @@ poll 和 select 在速度上都很慢。 几乎所有的系统都支持 select,但是只有比较新的系统支持 poll。 -## epoll 工作模式 +## eopll 工作模式 epoll_event 有两种触发模式:LT(level trigger)和 ET(edge trigger)。 diff --git a/notes/数据库系统原理.md b/notes/数据库系统原理.md index d7e3d8d6..1d493176 100644 --- a/notes/数据库系统原理.md +++ b/notes/数据库系统原理.md @@ -378,7 +378,7 @@ Next-Key Locks 也是 MySQL 的 InnoDB 存储引擎的一种锁实现。MVCC 不 锁定整个记录(行)。锁定的对象是记录的索引,而不是记录本身。如果表没有设置索引,InnoDB 会自动在主键上创建隐藏的聚集索引,因此 Record Locks 依然可以使用。 -## Grap Locks +## Gap Locks 锁定一个范围内的索引,例如当一个事务执行以下语句,其它事务就不能在 t.c 中插入 15。 From 849f8d2a2cbc285326ace273bb866b63aebbf803 Mon Sep 17 00:00:00 2001 From: CyC2018 <1029579233@qq.com> Date: Mon, 16 Jul 2018 00:01:10 +0800 Subject: [PATCH 10/26] auto commit --- notes/Socket.md | 122 +++++++++++++++++++----------------------------- 1 file changed, 49 insertions(+), 73 deletions(-) diff --git a/notes/Socket.md b/notes/Socket.md index 13f1eaf5..8e7c0b89 100644 --- a/notes/Socket.md +++ b/notes/Socket.md @@ -10,9 +10,9 @@ * [二、I/O 复用](#二io-复用) * [select](#select) * [poll](#poll) + * [比较](#比较) * [epoll](#epoll) - * [select 和 poll 比较](#select-和-poll-比较) - * [eopll 工作模式](#eopll-工作模式) + * [工作模式](#工作模式) * [应用场景](#应用场景) * [参考资料](#参考资料) @@ -106,17 +106,11 @@ select/poll/epoll 都是 I/O 多路复用的具体实现,select 出现的最 int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); ``` -readset, writeset, exceptset 参数,分别对应读、写、异常条件的描述符集合。 +有三种类型的描述符类型:readset、writeset、exceptset,分别对应读、写、异常条件的描述符集合。fd_set 使用数组实现,数组大小使用 FD_SETSIZE 定义。 -timeout 参数告知内核等待所指定描述符中的任何一个就绪的最长时间; +timeout 为超时参数,调用 select 会一直阻塞直到有描述符的事件到达或者等待的时间超过 timeout。 -成功调用返回结果大于 0;出错返回结果为 -1;超时返回结果为 0。 - -每次调用 select 都需要将 readfds, writefds, exceptfds 链表内容全部从应用进程缓冲区复制到内核缓冲区。 - -返回结果中内核并没有声明 fd_set 中哪些描述符已经准备好,所以如果返回值大于 0 时,应用进程需要遍历所有的 fd_set。 - -select 最多支持 1024 个描述符,其中 1024 由内核的 FD_SETSIZE 决定。如果需要打破该限制可以修改 FD_SETSIZE,然后重新编译内核。 +成功调用返回结果大于 0,出错返回结果为 -1,超时返回结果为 0。 ```c fd_set fd_in, fd_out; @@ -163,21 +157,7 @@ else int poll(struct pollfd *fds, unsigned int nfds, int timeout); ``` -```c -struct pollfd { - int fd; // 文件描述符 - short events; // 监视的请求事件 - short revents; // 已发生的事件 -}; -``` - -它和 select 功能基本相同,同样需要每次都将描述符从应用进程缓冲区复制到内核缓冲区,调用返回后同样需要进行轮询才能知道哪些描述符已经准备好。 - -poll 取消了 1024 个描述符数量上限,但是数量太大以后不能保证执行效率,因为复制大量内存到内核十分低效,所需时间与描述符数量成正比。 - -poll 在描述符的重复利用上比 select 的 fd_set 会更好。 - -如果在多线程下,如果一个线程对某个描述符调用了 poll 系统调用,但是另一个线程关闭了该描述符,会导致 poll 调用结果不确定,该问题同样出现在 select 中。 +pollfd 使用链表实现。 ```c // The structure for two events @@ -211,6 +191,28 @@ else } ``` +## 比较 + +### 1. 功能 + +select 和 poll 的功能基本相同,不过在一些实现细节上有所不同。 + +- select 会修改描述符,而 poll 不会; +- select 的描述符类型使用数组实现,FD_SETSIZE 大小默认为 1024,因此默认只能监听 1024 个描述符。如果要监听更多描述符的话,需要修改 FD_SETSIZE 之后重新编译;而 poll 的描述符类型使用链表实现,没有描述符的数量的限制; +- poll 提供了更多的事件类型,并且对描述符的重复利用上比 select 高。 +- 如果一个线程对某个描述符调用了 select 或者 poll,另一个线程关闭了该描述符,会导致调用结果不确定。 + +### 2. 速度 + +select 和 poll 速度都比较慢。 + +- select 和 poll 每次调用都需要将全部描述符从应用进程缓冲区复制到内核缓冲区。 +- select 和 poll 的返回结果中没有声明哪些描述符已经准备好,所以如果返回值大于 0 时,应用进程都需要使用轮询的方式来找到 I/O 完成的描述符。 + +### 3. 可移植性 + +几乎所有的系统都支持 select,但是只有比较新的系统支持 poll。 + ## epoll ```c @@ -219,21 +221,15 @@ int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout); ``` +epoll_ctl() 用于向内核注册新的描述符或者是改变某个文件描述符的状态。已注册的描述符在内核中会被维护在一棵红黑树上,通过回调函数内核会将 I/O 准备好的描述符加入到一个链表中管理,进程调用 epoll_wait() 便可以得到事件完成的描述符。 + +从上面的描述可以看出,epoll 只需要将描述符从进程缓冲区向内核缓冲区拷贝一次,并且进程不需要通过轮询来获得事件完成的描述符。 + epoll 仅适用于 Linux OS。 -它是 select 和 poll 的增强版,更加灵活而且没有描述符数量限制。 +epoll 比 select 和 poll 更加灵活而且没有描述符数量限制。 -它将用户关心的描述符放到内核的一个事件表中,从而只需要在用户进程缓冲区和内核缓冲区拷贝一次。 - -select 和 poll 方式中,进程只有在调用一定的方法后,内核才对所有监视的描述符进行扫描。而 epoll 事先通过 epoll_ctl() 来注册描述符,一旦基于某个描述符就绪时,内核会采用类似 callback 的回调机制,迅速激活这个描述符,当进程调用 epoll_wait() 时便得到通知。 - -新版本的 epoll_create(int size) 参数 size 不起任何作用,在旧版本的 epoll 中如果描述符的数量大于 size,不保证服务质量。 - -epoll_ctl() 执行一次系统调用,用于向内核注册新的描述符或者是改变某个文件描述符的状态。已注册的描述符在内核中会被维护在一棵红黑树上,通过回调函数内核会将 I/O 准备好的描述符加入到一个链表中管理。 - -epoll_wait() 取出在内核中通过链表维护的 I/O 准备好的描述符,将他们从内核复制到应用进程中,不需要像 select/poll 对注册的所有描述符遍历一遍。 - -epoll 对多线程编程更有友好,同时多个线程对同一个描述符调用了 epoll_wait() 也不会产生像 select/poll 的不确定情况。或者一个线程调用了 epoll_wait() 另一个线程关闭了同一个描述符也不会产生不确定情况。 +epoll 对多线程编程更有友好,一个线程调用了 epoll_wait() 另一个线程关闭了同一个描述符也不会产生像 select 和 poll 的不确定情况。 ```c // Create the epoll descriptor. Only one is needed per app, and is used to monitor all sockets. @@ -282,64 +278,42 @@ else } ``` -## select 和 poll 比较 -### 1. 功能 +## 工作模式 -它们提供了几乎相同的功能,但是在一些细节上有所不同: - -- select 会修改 fd_set 参数,而 poll 不会; -- select 默认只能监听 1024 个描述符,如果要监听更多的话,需要修改 FD_SETSIZE 之后重新编译; -- poll 提供了更多的事件类型。 - -### 2. 速度 - -poll 和 select 在速度上都很慢。 - -- 它们都采取轮询的方式来找到 I/O 完成的描述符,如果描述符很多,那么速度就会很慢; -- select 只使用每个描述符的 3 位,而 poll 通常需要使用 64 位,因此 poll 需要在用户进程和内核之间复制更多的数据。 - -### 3. 可移植性 - -几乎所有的系统都支持 select,但是只有比较新的系统支持 poll。 - -## eopll 工作模式 - -epoll_event 有两种触发模式:LT(level trigger)和 ET(edge trigger)。 +epoll 的描述符事件有两种触发模式:LT(level trigger)和 ET(edge trigger)。 ### 1. LT 模式 -当 epoll_wait() 检测到描述符事件发生并将此事件通知应用程序,应用程序可以不立即处理该事件。下次调用 epoll_wait() 时,会再次响应应用程序并通知此事件。是默认的一种模式,并且同时支持 Blocking 和 No-Blocking。 +当 epoll_wait() 检测到描述符事件到达时,将此事件通知进程,进程可以不立即处理该事件,下次调用 epoll_wait() 会再次通知进程。是默认的一种模式,并且同时支持 Blocking 和 No-Blocking。 ### 2. ET 模式 -当 epoll_wait() 检测到描述符事件发生并将此事件通知应用程序,应用程序必须立即处理该事件。如果不处理,下次调用 epoll_wait() 时,不会再次响应应用程序并通知此事件。很大程度上减少了 epoll 事件被重复触发的次数,因此效率要比 LT 模式高。只支持 No-Blocking,以避免由于一个文件句柄的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死。 +和 LT 模式不同的是,通知之后进程必须立即处理事件,下次再调用 epoll_wait() 时不会再得到事件到达的通知。 + +很大程度上减少了 epoll 事件被重复触发的次数,因此效率要比 LT 模式高。只支持 No-Blocking,以避免由于一个文件句柄的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死。 ## 应用场景 -很容易产生一种错觉认为只要用 epoll 就可以了,select poll 都是历史遗留问题,并没有什么应用场景,其实并不是这样的。 +很容易产生一种错觉认为只要用 epoll 就可以了,select 和 poll 都已经过时了,其实它们都有各自的使用场景。 ### 1. select 应用场景 -select() poll() epoll_wait() 都有一个 timeout 参数,在 select() 中 timeout 的精确度为 1ns,而 poll() 和 epoll_wait() 中则为 1ms。所以 select 更加适用于实时要求更高的场景,比如核反应堆的控制。 +select 的 timeout 参数精度为 1ns,而 poll 和 epoll 为 1ms,因此 select 更加适用于实时要求更高的场景,比如核反应堆的控制。 -select 历史更加悠久,它的可移植性更好,几乎被所有主流平台所支持。 +select 可移植性更好,几乎被所有主流平台所支持。 ### 2. poll 应用场景 -poll 没有最大描述符数量的限制,如果平台支持应该采用 poll 且对实时性要求并不是十分严格,而不是 select。 +poll 没有最大描述符数量的限制,如果平台支持并且对实时性要求不高,应该使用 poll 而不是 select。 -需要同时监控小于 1000 个描述符。没有必要使用 epoll,因为这个应用场景下并不能体现 epoll 的优势。 +需要同时监控小于 1000 个描述符,就没有必要使用 epoll,因为这个应用场景下并不能体现 epoll 的优势。 -需要监控的描述符状态变化多,而且都是非常短暂的。因为 epoll 中的所有描述符都存储在内核中,造成每次需要对描述符的状态改变都需要通过 epoll_ctl() 进行系统调用,频繁系统调用降低效率。并且epoll 的描述符存储在内核,不容易调试。 +需要监控的描述符状态变化多,而且都是非常短暂的,也没有必要使用 epoll。因为 epoll 中的所有描述符都存储在内核中,造成每次需要对描述符的状态改变都需要通过 epoll_ctl() 进行系统调用,频繁系统调用降低效率。并且epoll 的描述符存储在内核,不容易调试。 ### 3. epoll 应用场景 -程序只需要运行在 Linux 平台上,有非常大量的描述符需要同时轮询,而且这些连接最好是长连接。 - -### 4. 性能对比 - -[epoll Scalability Web Page](http://lse.sourceforge.net/epoll/index.html) +只需要运行在 Linux 平台上,并且有非常大量的描述符需要同时轮询,而且这些连接最好是长连接。 # 参考资料 @@ -348,3 +322,5 @@ poll 没有最大描述符数量的限制,如果平台支持应该采用 poll - [Synchronous and Asynchronous I/O](https://msdn.microsoft.com/en-us/library/windows/desktop/aa365683(v=vs.85).aspx) - [Linux IO 模式及 select、poll、epoll 详解](https://segmentfault.com/a/1190000003063859) - [poll vs select vs event-based](https://daniel.haxx.se/docs/poll-vs-select.html) +- [select / poll / epoll: practical difference for system architects](http://www.ulduzsoft.com/2014/01/select-poll-epoll-practical-difference-for-system-architects/) +- [Browse the source code of userspace/glibc/sysdeps/unix/sysv/linux/ online](https://code.woboq.org/userspace/glibc/sysdeps/unix/sysv/linux/) From 77ebdf2a8da519a5ba685775a6af1f8b1920d05a Mon Sep 17 00:00:00 2001 From: zach <1374698112@qq.com> Date: Mon, 16 Jul 2018 11:33:20 +0800 Subject: [PATCH 11/26] Update HTTP.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 单词拼写错误 --- notes/HTTP.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notes/HTTP.md b/notes/HTTP.md index 7d64c901..57b8aa63 100644 --- a/notes/HTTP.md +++ b/notes/HTTP.md @@ -66,7 +66,7 @@ ## URL -- URI(Uniform Resource Indentifier,统一资源标识符) +- URI(Uniform Resource Identifier,统一资源标识符) - URL(Uniform Resource Locator,统一资源定位符) - URN(Uniform Resource Name,统一资源名称),例如 urn:isbn:0-486-27557-4。 From 8be930feb968e27770c4d3c9b3cab792b5e4bbb8 Mon Sep 17 00:00:00 2001 From: zach <1374698112@qq.com> Date: Mon, 16 Jul 2018 14:41:28 +0800 Subject: [PATCH 12/26] Update HTTP.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 单词错误 --- notes/HTTP.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notes/HTTP.md b/notes/HTTP.md index 57b8aa63..6fec1df9 100644 --- a/notes/HTTP.md +++ b/notes/HTTP.md @@ -232,7 +232,7 @@ CONNECT www.example.com:443 HTTP/1.1 - **500 Internal Server Error** :服务器正在执行请求时发生错误。 -- **503 Service Unavilable** :服务器暂时处于超负载或正在进行停机维护,现在无法处理请求。 +- **503 Service Unavailable** :服务器暂时处于超负载或正在进行停机维护,现在无法处理请求。 # 四、HTTP 首部 From a6e03d3de3ec4f1cbd2499fc3c40013e28c58a1d Mon Sep 17 00:00:00 2001 From: ChinShengHao <40648580+ChinShengHao@users.noreply.github.com> Date: Tue, 17 Jul 2018 17:56:18 +0800 Subject: [PATCH 13/26] Update SUMMARY.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 之前:当网址有空白键时,网络链接(Hyperlink)功能无效。 现在:就算网址有空白键,网络链接(Hyperlink)也能正常操作。 解决方法:将空白键换成%20 即可。 --- SUMMARY.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/SUMMARY.md b/SUMMARY.md index e60f018e..ad73c3ed 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -1,8 +1,8 @@ # Summary * 算法 - * [剑指 Offer 题解](/notes/剑指 offer 题解.md) - * [Leetcode 题解](/notes/Leetcode 题解.md) + * [剑指 Offer 题解](/notes/剑指%20offer%20题解.md) + * [Leetcode 题解](/notes/Leetcode%20题解.md) * [算法](/notes/算法.md) * 操作系统 * [计算机操作系统](/notes/计算机操作系统.md) @@ -17,15 +17,15 @@ * 数据库 * [数据库系统原理](/notes/数据库系统原理.md) * [SQL](/notes/SQL.md) - * [Leetcode-Database 题解](/notes/Leetcode-Database 题解.md) + * [Leetcode-Database 题解](/notes/Leetcode-Database%20题解.md) * [MySQL](/notes/MySQL.md) * [Redis](/notes/Redis.md) * Java - * [Java 基础](/notes/Java 基础.md) - * [Java 虚拟机](/notes/Java 虚拟机.md) - * [Java 并发](/notes/Java 并发.md) - * [Java 容器](/notes/Java 容器.md) - * [Java I/O](/notes/Java I/O.md) + * [Java 基础](/notes/Java%20基础.md) + * [Java 虚拟机](/notes/Java%20虚拟机.md) + * [Java 并发](/notes/Java%20并发.md) + * [Java 容器](/notes/Java%20容器.md) + * [Java I/O](/notes/Java%20I/O.md) * 分布式 * [一致性](/notes/一致性.md) * [分布式问题分析](/notes/分布式问题分析.md) From f8d65d283e3ad750231eb0a4ae1a3af348c3be6b Mon Sep 17 00:00:00 2001 From: ChinShengHao <40648580+ChinShengHao@users.noreply.github.com> Date: Tue, 17 Jul 2018 18:00:19 +0800 Subject: [PATCH 14/26] Update SUMMARY.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 更改部分:Java I/O 的网址输入错误,已更换。 --- SUMMARY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SUMMARY.md b/SUMMARY.md index ad73c3ed..8dcc8e7d 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -25,7 +25,7 @@ * [Java 虚拟机](/notes/Java%20虚拟机.md) * [Java 并发](/notes/Java%20并发.md) * [Java 容器](/notes/Java%20容器.md) - * [Java I/O](/notes/Java%20I/O.md) + * [Java I/O](/notes/Java%20IO.md) * 分布式 * [一致性](/notes/一致性.md) * [分布式问题分析](/notes/分布式问题分析.md) From 662238e8d2255560b4e2bd8108fc30a490ce825b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=83=91=E6=B0=B8=E5=B7=9D?= <1029579233@qq.com> Date: Tue, 17 Jul 2018 21:37:06 +0800 Subject: [PATCH 15/26] Update SUMMARY.md --- SUMMARY.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/SUMMARY.md b/SUMMARY.md index 8dcc8e7d..e60f018e 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -1,8 +1,8 @@ # Summary * 算法 - * [剑指 Offer 题解](/notes/剑指%20offer%20题解.md) - * [Leetcode 题解](/notes/Leetcode%20题解.md) + * [剑指 Offer 题解](/notes/剑指 offer 题解.md) + * [Leetcode 题解](/notes/Leetcode 题解.md) * [算法](/notes/算法.md) * 操作系统 * [计算机操作系统](/notes/计算机操作系统.md) @@ -17,15 +17,15 @@ * 数据库 * [数据库系统原理](/notes/数据库系统原理.md) * [SQL](/notes/SQL.md) - * [Leetcode-Database 题解](/notes/Leetcode-Database%20题解.md) + * [Leetcode-Database 题解](/notes/Leetcode-Database 题解.md) * [MySQL](/notes/MySQL.md) * [Redis](/notes/Redis.md) * Java - * [Java 基础](/notes/Java%20基础.md) - * [Java 虚拟机](/notes/Java%20虚拟机.md) - * [Java 并发](/notes/Java%20并发.md) - * [Java 容器](/notes/Java%20容器.md) - * [Java I/O](/notes/Java%20IO.md) + * [Java 基础](/notes/Java 基础.md) + * [Java 虚拟机](/notes/Java 虚拟机.md) + * [Java 并发](/notes/Java 并发.md) + * [Java 容器](/notes/Java 容器.md) + * [Java I/O](/notes/Java I/O.md) * 分布式 * [一致性](/notes/一致性.md) * [分布式问题分析](/notes/分布式问题分析.md) From 95fd87775ed4fe73726b4a1fb455b0ce9d5f39dd Mon Sep 17 00:00:00 2001 From: Gouden Date: Tue, 17 Jul 2018 22:52:13 +0800 Subject: [PATCH 16/26] =?UTF-8?q?Update=20Java=20=E5=B9=B6=E5=8F=91.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- notes/Java 并发.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notes/Java 并发.md b/notes/Java 并发.md index 9c55c715..3d8eecac 100644 --- a/notes/Java 并发.md +++ b/notes/Java 并发.md @@ -1232,7 +1232,7 @@ Thread 对象的 start() 方法调用先行发生于此线程的每一个动作 > Thread Join Rule -join() 方法返回先行发生于 Thread 对象的结束。 +Thread 对象的结束先行发生于join() 方法返回 。

From cc82b238c09a1fcc28b39ffe165c51933b8f0546 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=83=91=E6=B0=B8=E5=B7=9D?= <1029579233@qq.com> Date: Wed, 18 Jul 2018 14:16:26 +0800 Subject: [PATCH 17/26] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 89625ec3..5624918d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ - +[![](https://img.shields.io/badge/_-gitter-blue.svg)](https://gitter.im/CyC2018-Interview-Notebook/Lobby?utm_source=share-link&utm_medium=link&utm_campaign=share-link) [![](https://img.shields.io/badge/>-gitbook-blue.svg)](https://legacy.gitbook.com/book/cyc2018/interview-notebook/details) + | Ⅰ | Ⅱ | Ⅲ | Ⅳ | Ⅴ | Ⅵ | Ⅶ | Ⅷ | Ⅸ | Ⅹ | | :--------: | :---------: | :---------: | :---------: | :---------: | :---------:| :---------: | :-------: | :-------:| :------:| | 算法[:pencil2:](#算法-pencil2) | 操作系统[:computer:](#操作系统-computer)|网络[:cloud:](#网络-cloud) | 面向对象[:couple:](#面向对象-couple) |数据库[:floppy_disk:](#数据库-floppy_disk)| Java [:coffee:](#java-coffee)| 分布式 [:sweat_drops:](#分布式-sweat_drops)| 工具[:hammer:](#工具-hammer)| 编码实践[:speak_no_evil:](#编码实践-speak_no_evil)| 后记[:memo:](#后记-memo) | From 0c1849351cadb2337225dd85ea61911ab344d813 Mon Sep 17 00:00:00 2001 From: hanlin Date: Wed, 18 Jul 2018 20:08:26 +0800 Subject: [PATCH 18/26] =?UTF-8?q?sql=E7=BB=83=E4=B9=A0=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- notes/sql经典练习题.sql | 432 +++++++++++++++++++++++++++++++++++ 1 file changed, 432 insertions(+) create mode 100644 notes/sql经典练习题.sql diff --git a/notes/sql经典练习题.sql b/notes/sql经典练习题.sql new file mode 100644 index 00000000..72965fa2 --- /dev/null +++ b/notes/sql经典练习题.sql @@ -0,0 +1,432 @@ +use fuxi; + +CREATE TABLE STUDENT +( + SNO VARCHAR(3) NOT NULL, + SNAME VARCHAR(4) NOT NULL, + SSEX VARCHAR(2) NOT NULL, + SBIRTHDAY DATETIME, + CLASS VARCHAR(5) +); + +CREATE TABLE COURSE +( + CNO VARCHAR(5) NOT NULL, + CNAME VARCHAR(10) NOT NULL, + TNO VARCHAR(10) NOT NULL +); + +CREATE TABLE SCORE +( + SNO VARCHAR(3) NOT NULL, + CNO VARCHAR(5) NOT NULL, + DEGREE NUMERIC(10, 1) NOT NULL +); + +CREATE TABLE TEACHER +( + TNO VARCHAR(3) NOT NULL, + TNAME VARCHAR(4) NOT NULL, + TSEX VARCHAR(2) NOT NULL, + TBIRTHDAY DATETIME NOT NULL, + PROF VARCHAR(6), + DEPART VARCHAR(10) NOT NULL +); + +INSERT INTO STUDENT (SNO, SNAME, SSEX, SBIRTHDAY, CLASS) VALUES (108, '曾华' + , '男', '1977-09-01', 95033); +INSERT INTO STUDENT (SNO, SNAME, SSEX, SBIRTHDAY, CLASS) VALUES (105, '匡明' + , '男', '1975-10-02', 95031); +INSERT INTO STUDENT (SNO, SNAME, SSEX, SBIRTHDAY, CLASS) VALUES (107, '王丽' + , '女', '1976-01-23', 95033); +INSERT INTO STUDENT (SNO, SNAME, SSEX, SBIRTHDAY, CLASS) VALUES (101, '李军' + , '男', '1976-02-20', 95033); +INSERT INTO STUDENT (SNO, SNAME, SSEX, SBIRTHDAY, CLASS) VALUES (109, '王芳' + , '女', '1975-02-10', 95031); +INSERT INTO STUDENT (SNO, SNAME, SSEX, SBIRTHDAY, CLASS) VALUES (103, '陆君' + , '男', '1974-06-03', 95031); + +INSERT INTO COURSE (CNO, CNAME, TNO) VALUES ('3-105', '计算机导论', 825); +INSERT INTO COURSE (CNO, CNAME, TNO) VALUES ('3-245', '操作系统', 804); +INSERT INTO COURSE (CNO, CNAME, TNO) VALUES ('6-166', '数据电路', 856); +INSERT INTO COURSE (CNO, CNAME, TNO) VALUES ('9-888', '高等数学', 100); + +INSERT INTO SCORE (SNO, CNO, DEGREE) VALUES (103, '3-245', 86); +INSERT INTO SCORE (SNO, CNO, DEGREE) VALUES (105, '3-245', 75); +INSERT INTO SCORE (SNO, CNO, DEGREE) VALUES (109, '3-245', 68); +INSERT INTO SCORE (SNO, CNO, DEGREE) VALUES (103, '3-105', 92); +INSERT INTO SCORE (SNO, CNO, DEGREE) VALUES (105, '3-105', 88); +INSERT INTO SCORE (SNO, CNO, DEGREE) VALUES (109, '3-105', 76); +INSERT INTO SCORE (SNO, CNO, DEGREE) VALUES (101, '3-105', 64); +INSERT INTO SCORE (SNO, CNO, DEGREE) VALUES (107, '3-105', 91); +INSERT INTO SCORE (SNO, CNO, DEGREE) VALUES (101, '6-166', 85); +INSERT INTO SCORE (SNO, CNO, DEGREE) VALUES (107, '6-106', 79); +INSERT INTO SCORE (SNO, CNO, DEGREE) VALUES (108, '3-105', 78); +INSERT INTO SCORE (SNO, CNO, DEGREE) VALUES (108, '6-166', 81); + +INSERT INTO TEACHER (TNO, TNAME, TSEX, TBIRTHDAY, PROF, DEPART) +VALUES (804, '李诚', '男', '1958-12-02', '副教授', '计算机系'); +INSERT INTO TEACHER (TNO, TNAME, TSEX, TBIRTHDAY, PROF, DEPART) +VALUES (856, '张旭', '男', '1969-03-12', '讲师', '电子工程系'); +INSERT INTO TEACHER (TNO, TNAME, TSEX, TBIRTHDAY, PROF, DEPART) +VALUES (825, '王萍', '女', '1972-05-05', '助教', '计算机系'); +INSERT INTO TEACHER (TNO, TNAME, TSEX, TBIRTHDAY, PROF, DEPART) +VALUES (831, '刘冰', '女', '1977-08-14', '助教', '电子工程系'); + +-- 1、 查询Student表中的所有记录的Sname、Ssex和Class列。 +select + SNAME, + SSEX, + CLASS +from STUDENT; + +-- 2、 查询教师所有的单位即不重复的Depart列。 +select distinct DEPART +from TEACHER1; + +-- 3、 查询Student表的所有记录。 +select * +from STUDENT; + +-- 4、 查询Score表中成绩在60到80之间的所有记录。 +select * +from SCORE +where DEGREE > 60 and DEGREE < 80; + +-- 5、 查询Score表中成绩为85,86或88的记录。 +select * +from SCORE +where DEGREE = 85 or DEGREE = 86 or DEGREE = 88; + +-- 6、 查询Student表中“95031”班或性别为“女”的同学记录。 +select * +from STUDENT +where CLASS = '95031' or SSEX = '女'; + +-- 7、 以Class降序查询Student表的所有记录。 +select * +from STUDENT +order by CLASS desc; + +-- 8、 以Cno升序、Degree降序查询Score表的所有记录。 +select * +from SCORE +order by CNO asc, DEGREE desc; + +-- 9、 查询“95031”班的学生人数。 +select count(*) +from STUDENT +where CLASS = '95031'; + +-- 10、查询Score表中的最高分的学生学号和课程号。 +select + sno, + CNO +from SCORE +where DEGREE = ( + select max(DEGREE) + from SCORE +); + +-- 11、查询‘3-105’号课程的平均分。 +select avg(DEGREE) +from SCORE +where CNO = '3-105'; + +-- 12、查询Score表中至少有5名学生选修的并以3开头的课程的平均分数。 +select + avg(DEGREE), + CNO +from SCORE +where cno like '3%' +group by CNO +having count(*) > 5; + +-- 13、查询最低分大于70,最高分小于90的Sno列。 +select SNO +from SCORE +group by SNO +having min(DEGREE) > 70 and max(DEGREE) < 90; + +-- 14、查询所有学生的Sname、Cno和Degree列。 +select + SNAME, + CNO, + DEGREE +from STUDENT, SCORE +where STUDENT.SNO = SCORE.SNO; + +-- 15、查询所有学生的Sno、Cname和Degree列。 +select + SCORE.SNO, + CNO, + DEGREE +from STUDENT, SCORE +where STUDENT.SNO = SCORE.SNO; + +-- 16、查询所有学生的Sname、Cname和Degree列。 +SELECT + A.SNAME, + B.CNAME, + C.DEGREE +FROM STUDENT A + JOIN (COURSE B, SCORE C) + ON A.SNO = C.SNO AND B.CNO = C.CNO; + +-- 17、查询“95033”班所选课程的平均分。 +select avg(DEGREE) +from SCORE +where sno in (select SNO + from STUDENT + where CLASS = '95033'); + +-- 18、假设使用如下命令建立了一个grade表: +create table grade ( + low numeric(3, 0), + upp numeric(3), + rank char(1) +); +insert into grade values (90, 100, 'A'); +insert into grade values (80, 89, 'B'); +insert into grade values (70, 79, 'C'); +insert into grade values (60, 69, 'D'); +insert into grade values (0, 59, 'E'); +-- 现查询所有同学的Sno、Cno和rank列。 +SELECT + A.SNO, + A.CNO, + B.RANK +FROM SCORE A, grade B +WHERE A.DEGREE BETWEEN B.LOW AND B.UPP +ORDER BY RANK; + +-- 19、查询选修“3-105”课程的成绩高于“109”号同学成绩的所有同学的记录。 +select * +from SCORE +where CNO = '3-105' and DEGREE > ALL ( + select DEGREE + from SCORE + where SNO = '109' +); + +set @@global.sql_mode = 'STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'; +set sql_mode = 'STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'; + +-- 20、查询score中选学一门以上课程的同学中分数为非最高分成绩的记录 +select * +from SCORE +where DEGREE < (select MAX(DEGREE) + from SCORE) +group by SNO +having count(*) > 1; + +-- 21、查询成绩高于学号为“109”、课程号为“3-105”的成绩的所有记录。 +-- 同19 + +-- 22、查询和学号为108的同学同年出生的所有学生的Sno、Sname和Sbirthday列。 +select + SNO, + SNAME, + SBIRTHDAY +from STUDENT +where year(SBIRTHDAY) = ( + select year(SBIRTHDAY) + from STUDENT + where SNO = '108' +); + +-- 23、查询“张旭“教师任课的学生成绩。 +select * +from SCORE +where cno = ( + select CNO + from COURSE + inner join TEACHER on COURSE.TNO = TEACHER.TNO and TNAME = '张旭' +); + +-- 24、查询选修某课程的同学人数多于5人的教师姓名。 +select TNAME +from TEACHER +where TNO = ( + select TNO + from COURSE + where CNO = (select CNO + from SCORE + group by CNO + having count(SNO) > 5) +); + +-- 25、查询95033班和95031班全体学生的记录。 +select * +from STUDENT +where CLASS in ('95033', '95031'); + +-- 26、查询存在有85分以上成绩的课程Cno. +select cno +from SCORE +group by CNO +having MAX(DEGREE) > 85; + +-- 27、查询出“计算机系“教师所教课程的成绩表。 +select * +from SCORE +where CNO in (select CNO + from TEACHER, COURSE + where DEPART = '计算机系' and COURSE.TNO = TEACHER.TNO); + +-- 28、查询“计算机系”与“电子工程系“不同职称的教师的Tname和Prof +select + tname, + prof +from TEACHER +where depart = '计算机系' and prof not in ( + select prof + from TEACHER + where depart = '电子工程系' +); + +-- 29、查询选修编号为“3-105“课程且成绩至少高于选修编号为“3-245”的同学的Cno、Sno和Degree,并按Degree从高到低次序排序。 +select + CNO, + SNO, + DEGREE +from SCORE +where CNO = '3-105' and DEGREE > any ( + select DEGREE + from SCORE + where CNO = '3-245' +) +order by DEGREE desc; + +-- 30、查询选修编号为“3-105”且成绩高于选修编号为“3-245”课程的同学的Cno、Sno和Degree. +SELECT * +FROM SCORE +WHERE DEGREE > ALL ( + SELECT DEGREE + FROM SCORE + WHERE CNO = '3-245' +) +ORDER by DEGREE desc; + +-- 31、查询所有教师和同学的name、sex和birthday. +select + TNAME name, + TSEX sex, + TBIRTHDAY birthday +from TEACHER +union +select + sname name, + SSEX sex, + SBIRTHDAY birthday +from STUDENT; + +-- 32、查询所有“女”教师和“女”同学的name、sex和birthday. +select + TNAME name, + TSEX sex, + TBIRTHDAY birthday +from TEACHER +where TSEX = '女' +union +select + sname name, + SSEX sex, + SBIRTHDAY birthday +from STUDENT +where SSEX = '女'; + +-- 33、查询成绩比该课程平均成绩低的同学的成绩表。 +SELECT A.* +FROM SCORE A +WHERE DEGREE < (SELECT AVG(DEGREE) + FROM SCORE B + WHERE A.CNO = B.CNO); + +-- 34、查询所有任课教师的Tname和Depart. +select + TNAME, + DEPART +from TEACHER a +where exists(select * + from COURSE b + where a.TNO = b.TNO); + +-- 35、查询所有未讲课的教师的Tname和Depart. +select + TNAME, + DEPART +from TEACHER a +where tno not in (select tno + from COURSE); + +-- 36、查询至少有2名男生的班号。 +select CLASS +from STUDENT +where SSEX = '男' +group by CLASS +having count(SSEX) > 1; + +-- 37、查询Student表中不姓“王”的同学记录。 +select * +from STUDENT +where SNAME not like "王%"; + +-- 38、查询Student表中每个学生的姓名和年龄。 +select + SNAME, + year(now()) - year(SBIRTHDAY) +from STUDENT; + +-- 39、查询Student表中最大和最小的Sbirthday日期值。 +select min(SBIRTHDAY) birthday +from STUDENT +union +select max(SBIRTHDAY) birthday +from STUDENT; + +-- 40、以班号和年龄从大到小的顺序查询Student表中的全部记录。 +select * +from STUDENT +order by CLASS desc, year(now()) - year(SBIRTHDAY) desc; + +-- 41、查询“男”教师及其所上的课程。 +select * +from TEACHER, COURSE +where TSEX = '男' and COURSE.TNO = TEACHER.TNO; + +-- 42、查询最高分同学的Sno、Cno和Degree列。 +select + sno, + CNO, + DEGREE +from SCORE +where DEGREE = (select max(DEGREE) + from SCORE); + +-- 43、查询和“李军”同性别的所有同学的Sname. +select sname +from STUDENT +where SSEX = (select SSEX + from STUDENT + where SNAME = '李军'); + +-- 44、查询和“李军”同性别并同班的同学Sname. +select sname +from STUDENT +where (SSEX, CLASS) = (select + SSEX, + CLASS + from STUDENT + where SNAME = '李军'); + +-- 45、查询所有选修“计算机导论”课程的“男”同学的成绩表 +select * +from SCORE, STUDENT +where SCORE.SNO = STUDENT.SNO and SSEX = '男' and CNO = ( + select CNO + from COURSE + where CNAME = '计算机导论'); + + + From f01ae3dbb2fc26f7b4b0700d1a84ce9306f4da7d Mon Sep 17 00:00:00 2001 From: qbosen Date: Thu, 19 Jul 2018 10:48:08 +0800 Subject: [PATCH 19/26] =?UTF-8?q?update=20Java=20=E5=9F=BA=E7=A1=80.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- notes/Java 基础.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/notes/Java 基础.md b/notes/Java 基础.md index af1c6530..bdd13aff 100644 --- a/notes/Java 基础.md +++ b/notes/Java 基础.md @@ -763,10 +763,10 @@ try { ``` ```html -java.lang.CloneNotSupportedException: CloneTest +java.lang.CloneNotSupportedException: CloneExample ``` -以上抛出了 CloneNotSupportedException,这是因为 CloneTest 没有实现 Cloneable 接口。 +以上抛出了 CloneNotSupportedException,这是因为 CloneExample 没有实现 Cloneable 接口。 ```java public class CloneExample implements Cloneable { From ace808f01aecd900dbfa6dc0c6fa68cd96d8d915 Mon Sep 17 00:00:00 2001 From: CyC2018 <1029579233@qq.com> Date: Thu, 19 Jul 2018 10:55:21 +0800 Subject: [PATCH 20/26] auto commit --- .../sql 经典练习题.sql | 0 {pics => other}/计算机网络脑图.png | Bin 2 files changed, 0 insertions(+), 0 deletions(-) rename notes/sql经典练习题.sql => other/sql 经典练习题.sql (100%) rename {pics => other}/计算机网络脑图.png (100%) diff --git a/notes/sql经典练习题.sql b/other/sql 经典练习题.sql similarity index 100% rename from notes/sql经典练习题.sql rename to other/sql 经典练习题.sql diff --git a/pics/计算机网络脑图.png b/other/计算机网络脑图.png similarity index 100% rename from pics/计算机网络脑图.png rename to other/计算机网络脑图.png From 095ce787215d308e60f7b868436a07a27042ae96 Mon Sep 17 00:00:00 2001 From: CyC2018 <1029579233@qq.com> Date: Thu, 19 Jul 2018 13:58:32 +0800 Subject: [PATCH 21/26] auto commit --- notes/HTTP.md | 20 +++++----- notes/Java 基础.md | 4 +- notes/Java 并发.md | 73 ++++++++++++++++++---------------- notes/Java 虚拟机.md | 22 ++++++---- notes/MySQL.md | 2 +- notes/数据库系统原理.md | 4 +- notes/计算机网络.md | 2 +- 7 files changed, 68 insertions(+), 59 deletions(-) diff --git a/notes/HTTP.md b/notes/HTTP.md index 6fec1df9..e6a823cc 100644 --- a/notes/HTTP.md +++ b/notes/HTTP.md @@ -66,7 +66,7 @@ ## URL -- URI(Uniform Resource Identifier,统一资源标识符) +- URI(Uniform Resource Indentifier,统一资源标识符) - URL(Uniform Resource Locator,统一资源定位符) - URN(Uniform Resource Name,统一资源名称),例如 urn:isbn:0-486-27557-4。 @@ -232,7 +232,7 @@ CONNECT www.example.com:443 HTTP/1.1 - **500 Internal Server Error** :服务器正在执行请求时发生错误。 -- **503 Service Unavailable** :服务器暂时处于超负载或正在进行停机维护,现在无法处理请求。 +- **503 Service Unavilable** :服务器暂时处于超负载或正在进行停机维护,现在无法处理请求。 # 四、HTTP 首部 @@ -313,7 +313,9 @@ CONNECT www.example.com:443 HTTP/1.1 HTTP 协议是无状态的,主要是为了让 HTTP 协议尽可能简单,使得它能够处理大量事务。HTTP/1.1 引入 Cookie 来保存状态信息。 -Cookie 是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。它用于告知服务端两个请求是否来自同一浏览器,并保持用户的登录状态。 +Cookie 是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器之后向同一服务器再次发起请求时被携带上,用于告知服务端两个请求是否来自同一浏览器。由于之后每次请求都会需要携带 Cookie 数据,因此会带来额外的性能开销(尤其是在移动环境下)。 + +Cookie 曾一度用于客户端数据的存储,因为当时并没有其它合适的存储办法而作为唯一的存储手段,但现在随着现代浏览器开始支持各种各样的存储方式,Cookie 渐渐被淘汰。新的浏览器 API 已经允许开发者直接将数据存储到本地,如使用 Web storage API (本地存储和会话存储)或 IndexedDB。 ### 1. 用途 @@ -321,8 +323,6 @@ Cookie 是服务器发送到用户浏览器并保存在本地的一小块数据 - 个性化设置(如用户自定义设置、主题等) - 浏览器行为跟踪(如跟踪分析用户行为等) -Cookie 曾一度用于客户端数据的存储,因为当时并没有其它合适的存储办法而作为唯一的存储手段,但现在随着现代浏览器开始支持各种各样的存储方式,Cookie 渐渐被淘汰。由于服务器指定 Cookie 后,浏览器的每次请求都会携带 Cookie 数据,会带来额外的性能开销(尤其是在移动环境下)。新的浏览器 API 已经允许开发者直接将数据存储到本地,如使用 Web storage API (本地存储和会话存储)或 IndexedDB。 - ### 2. 创建过程 服务器发送的响应报文包含 Set-Cookie 首部字段,客户端得到响应报文后把 Cookie 内容保存到浏览器中。 @@ -367,7 +367,7 @@ console.log(document.cookie); 标记为 Secure 的 Cookie 只应通过被 HTTPS 协议加密过的请求发送给服务端。但即便设置了 Secure 标记,敏感信息也不应该通过 Cookie 传输,因为 Cookie 有其固有的不安全性,Secure 标记也无法提供确实的安全保障。 -标记为 HttpOnly 的 Cookie 不能被 JavaScript 脚本调用。因为跨域脚本 (XSS) 攻击常常使用 JavaScript 的 `Document.cookie` API 窃取用户的 Cookie 信息,因此使用 HttpOnly 标记可以在一定程度上避免 XSS 攻击。 +标记为 HttpOnly 的 Cookie 不能被 JavaScript 脚本调用。因为跨站脚本攻击 (XSS) 常常使用 JavaScript 的 `Document.cookie` API 窃取用户的 Cookie 信息,因此使用 HttpOnly 标记可以在一定程度上避免 XSS 攻击。 ```html Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly @@ -387,7 +387,7 @@ Path 标识指定了主机下的哪些路径可以接受 Cookie(该 URL 路径 除了可以将用户信息通过 Cookie 存储在用户浏览器中,也可以利用 Session 存储在服务器端,存储在服务器端的信息更加安全。 -Session 可以存储在服务器上的文件、数据库或者内存中,现在最常见的是将 Session 存储在内存型数据库中,比如 Redis。 +Session 可以存储在服务器上的文件、数据库或者内存中。也可以将 Session 存储在内存型数据库中,比如 Redis。 使用 Session 维护用户登录的过程如下: @@ -485,7 +485,7 @@ ETag: "82e22293907ce725faf67773957acd12" If-None-Match: "82e22293907ce725faf67773957acd12" ``` -Last-Modified 首部字段也可以用于缓存验证,它包含在源服务器发送的响应报文中,指示源服务器对资源的最后修改时间。但是它是一种弱校验器,因为只能精确到一秒,所以它通常作为 ETag 的备用方案。如果响应首部字段里含有这个信息,客户端可以在后续的请求中带上 If-Modified-Since 来验证缓存。服务器只在所请求的资源在给定的日期时间之后对内容进行过修改的情况下才会将资源返回,状态码为 200 OK。如果请求的资源从那时起未经修改,那么返回一个不带有消息主体的 304 Not Modified 响应, +Last-Modified 首部字段也可以用于缓存验证,它包含在源服务器发送的响应报文中,指示源服务器对资源的最后修改时间。但是它是一种弱校验器,因为只能精确到一秒,所以它通常作为 ETag 的备用方案。如果响应首部字段里含有这个信息,客户端可以在后续的请求中带上 If-Modified-Since 来验证缓存。服务器只在所请求的资源在给定的日期时间之后对内容进行过修改的情况下才会将资源返回,状态码为 200 OK。如果请求的资源从那时起未经修改,那么返回一个不带有消息主体的 304 Not Modified 响应。 ```html Last-Modified: Wed, 21 Oct 2015 07:28:00 GMT @@ -507,7 +507,7 @@ If-Modified-Since: Wed, 21 Oct 2015 07:28:00 GMT ### 2. 流水线 -默认情况下,HTTP 请求是按顺序发出的,下一个请求只有在当前请求收到应答过后才会被发出。由于会受到网络延迟和带宽的限制,在下一个请求被发送到服务器之前,可能需要等待很长时间。 +默认情况下,HTTP 请求是按顺序发出的,下一个请求只有在当前请求收到相应之后才会被发出。由于会受到网络延迟和带宽的限制,在下一个请求被发送到服务器之前,可能需要等待很长时间。 流水线是在同一条长连接上发出连续的请求,而不用等待响应返回,这样可以避免连接延迟。 @@ -627,7 +627,7 @@ HTTP/1.1 使用虚拟主机技术,使得一台服务器拥有多个域名, - 网络访问控制 - 访问日志记录 -代理服务器分为正向代理和反向代理两种,用户察觉得到正向代理的存在,而反向代理一般位于内部网络中,用户察觉不到。 +代理服务器分为正向代理和反向代理两种,用户察觉得到正向代理的存在;而反向代理一般位于内部网络中,用户察觉不到。

diff --git a/notes/Java 基础.md b/notes/Java 基础.md index bdd13aff..af1c6530 100644 --- a/notes/Java 基础.md +++ b/notes/Java 基础.md @@ -763,10 +763,10 @@ try { ``` ```html -java.lang.CloneNotSupportedException: CloneExample +java.lang.CloneNotSupportedException: CloneTest ``` -以上抛出了 CloneNotSupportedException,这是因为 CloneExample 没有实现 Cloneable 接口。 +以上抛出了 CloneNotSupportedException,这是因为 CloneTest 没有实现 Cloneable 接口。 ```java public class CloneExample implements Cloneable { diff --git a/notes/Java 并发.md b/notes/Java 并发.md index 3d8eecac..800796db 100644 --- a/notes/Java 并发.md +++ b/notes/Java 并发.md @@ -23,7 +23,8 @@ * [五、互斥同步](#五互斥同步) * [synchronized](#synchronized) * [ReentrantLock](#reentrantlock) - * [synchronized 和 ReentrantLock 比较](#synchronized-和-reentrantlock-比较) + * [比较](#比较) + * [使用选择](#使用选择) * [六、线程之间的协作](#六线程之间的协作) * [join()](#join) * [wait() notify() notifyAll()](#wait-notify-notifyall) @@ -498,6 +499,8 @@ public synchronized static void fun() { ## ReentrantLock +ReentrantLock 是 java.util.concurrent(J.U.C)包中的锁. + ```java public class LockExample { @@ -529,23 +532,8 @@ public static void main(String[] args) { 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 ``` -ReentrantLock 是 java.util.concurrent(J.U.C)包中的锁,相比于 synchronized,它多了以下高级功能: -**1. 等待可中断** - -当持有锁的线程长期不释放锁的时候,正在等待的线程可以选择放弃等待,改为处理其他事情。 - -**2. 可实现公平锁** - -公平锁是指多个线程在等待同一个锁时,必须按照申请锁的时间顺序来依次获得锁。 - -synchronized 中的锁是非公平的,ReentrantLock 默认情况下也是非公平的,但可以通过带布尔值的构造函数要求使用公平锁。 - -**3. 锁绑定多个条件** - -一个 ReentrantLock 对象可以同时绑定多个 Condition 对象。 - -## synchronized 和 ReentrantLock 比较 +## 比较 **1. 锁的实现** @@ -553,13 +541,25 @@ synchronized 是 JVM 实现的,而 ReentrantLock 是 JDK 实现的。 **2. 性能** -新版本 Java 对 synchronized 进行了很多优化,例如自旋锁等。目前来看它和 ReentrantLock 的性能基本持平了,因此性能因素不再是选择 ReentrantLock 的理由。synchronized 有更大的性能优化空间,应该优先考虑 synchronized。 +新版本 Java 对 synchronized 进行了很多优化,例如自旋锁等,synchronized 与 ReentrantLock 大致相同。 -**3. 功能** +**3. 等待可中断** -ReentrantLock 多了一些高级功能。 +当持有锁的线程长期不释放锁的时候,正在等待的线程可以选择放弃等待,改为处理其他事情。 -**4. 使用选择** +ReentrantLock 可中断,而 synchronized 不行。 + +**4. 公平锁** + +公平锁是指多个线程在等待同一个锁时,必须按照申请锁的时间顺序来依次获得锁。 + +synchronized 中的锁是非公平的,ReentrantLock 默认情况下也是非公平的,但是也可以是公平的。 + +**5. 锁绑定多个条件** + +一个 ReentrantLock 可以同时绑定多个 Condition 对象。 + +## 使用选择 除非需要使用 ReentrantLock 的高级功能,否则优先使用 synchronized。这是因为 synchronized 是 JVM 实现的一种锁机制,JVM 原生地支持它,而 ReentrantLock 不是所有的 JDK 版本都支持。并且使用 synchronized 不用担心没有释放锁而导致死锁问题,因为 JVM 会确保锁的释放。 @@ -1232,7 +1232,7 @@ Thread 对象的 start() 方法调用先行发生于此线程的每一个动作 > Thread Join Rule -Thread 对象的结束先行发生于join() 方法返回 。 +Thread 对象的结束先行发生于 join() 方法返回。

@@ -1240,7 +1240,7 @@ Thread 对象的结束先行发生于join() 方法返回 。 > Thread Interruption Rule -对线程 interrupt() 方法的调用先行发生于被中断线程的代码检测到中断事件的发生,可以通过 Thread.interrupted() 方法检测到是否有中断发生。 +对线程 interrupt() 方法的调用先行发生于被中断线程的代码检测到中断事件的发生,可以通过 interrupted() 方法检测到是否有中断发生。 ### 7. 对象终结规则 @@ -1385,22 +1385,21 @@ synchronized 和 ReentrantLock。 ### 2. 非阻塞同步 -互斥同步最主要的问题就是进行线程阻塞和唤醒所带来的性能问题,因此这种同步也称为阻塞同步(Blocking Synchronization)。 +互斥同步最主要的问题就是进行线程阻塞和唤醒所带来的性能问题,因此这种同步也称为阻塞同步。 -从处理问题的方式上说,互斥同步属于一种悲观的并发策略,总是认为只要不去做正确的同步措施(例如加锁),那就肯定会出现问题,无论共享数据是否真的会出现竞争,它都要进行加锁(这里讨论的是概念模型,实际上虚拟机会优化掉很大一部分不必要的加锁)、用户态核心态转换、维护锁计数器和检查是否有被阻塞的线程需要唤醒等操作。 +互斥同步属于一种悲观的并发策略,总是认为只要不去做正确的同步措施,那就肯定会出现问题。论共享数据是否真的会出现竞争,它都要进行加锁(这里讨论的是概念模型,实际上虚拟机会优化掉很大一部分不必要的加锁)、用户态核心态转换、维护锁计数器和检查是否有被阻塞的线程需要唤醒等操作。 -随着硬件指令集的发展,我们有了另外一个选择:基于冲突检测的乐观并发策略,通俗地说,就是先进行操作,如果没有其他线程争用共享数据,那操作就成功了;如果共享数据有争用,产生了冲突,那就再采取其他的补偿措施(最常见的补偿措施就是不断地重试,直到成功为止),这种乐观的并发策略的许多实现都不需要把线程挂起,因此这种同步操作称为非阻塞同步(Non-Blocking Synchronization)。 +随着硬件指令集的发展,我们可以使用基于冲突检测的乐观并发策略:先进行操作,如果没有其它线程争用共享数据,那操作就成功了,否则采取补偿措施(不断地重试,直到成功为止)。这种乐观的并发策略的许多实现都不需要把线程挂起,因此这种同步操作称为非阻塞同步。 -乐观锁需要操作和冲突检测这两个步骤具备原子性,这里就不能再使用互斥同步来保证了,只能靠硬件来完成。硬件支持的原子性操作最典型的是:比较并交换(Compare-and-Swap,CAS)。 +乐观锁需要操作和冲突检测这两个步骤具备原子性,这里就不能再使用互斥同步来保证了,只能靠硬件来完成。 -CAS 指令需要有 3 个操作数,分别是内存位置(在 Java 中可以简单理解为变量的内存地址,用 V 表示)、旧的预期值(用 A 表示)和新值(用 B 表示)。CAS 指令执行时,当且仅当 V 符合旧预期值 A 时,处理器用新值 B 更新 V 的值,否则它就不执行更新。但是无论是否更新了 V 的值,都会返回 V 的旧值,上述的处理过程是一个原子操作。 +硬件支持的原子性操作最典型的是:比较并交换(Compare-and-Swap,CAS)。CAS 指令需要有 3 个操作数,分别是内存地址 V、旧的预期值 A 和新值 B。当执行操作时,只有当 V 的值等于 A,才将 V 的值更新为 B。 J.U.C 包里面的整数原子类 AtomicInteger,其中的 compareAndSet() 和 getAndIncrement() 等方法都使用了 Unsafe 类的 CAS 操作。 -在下面的代码 1 中,使用了 AtomicInteger 执行了自增的操作。代码 2 是 incrementAndGet() 的源码,它调用了 unsafe 的 getAndAddInt() 。代码 3 是 getAndAddInt() 源码,var1 指示内存位置,var2 指示新值,var4 指示操作需要加的数值,这里为 1。在代码 3 的实现中,通过 getIntVolatile(var1, var2) 得到旧的预期值。通过调用 compareAndSwapInt() 来进行 CAS 比较,如果 var2=var5,那么就更新内存地址为 var1 的变量为 var5+var4。可以看到代码 3 是在一个循环中进行,发生冲突的做法是不断的进行重试。 +以下代码使用了 AtomicInteger 执行了自增的操作。 ```java -// 代码 1 private AtomicInteger cnt = new AtomicInteger(); public void add() { @@ -1408,15 +1407,17 @@ public void add() { } ``` +以下代码是 incrementAndGet() 的源码,它调用了 unsafe 的 getAndAddInt() 。 + ```java -// 代码 2 public final int incrementAndGet() { return unsafe.getAndAddInt(this, valueOffset, 1) + 1; } ``` +以下代码是 getAndAddInt() 源码,var1 指示内存地址,var2 指示旧值,var4 指示操作需要加的数值,这里为 1。通过 getIntVolatile(var1, var2) 得到旧的预期值,通过调用 compareAndSwapInt() 来进行 CAS 比较,如果 var2==var5,那么就更新内存地址为 var1 的变量为 var5+var4。可以看到 getAndAddInt() 在一个循环中进行,发生冲突的做法是不断的进行重试。 + ```java -// 代码 3 public final int getAndAddInt(Object var1, long var2, int var4) { int var5; do { @@ -1427,7 +1428,9 @@ public final int getAndAddInt(Object var1, long var2, int var4) { } ``` -ABA :如果一个变量初次读取的时候是 A 值,它的值被改成了 B,后来又被改回为 A,那 CAS 操作就会误认为它从来没有被改变过。J.U.C 包提供了一个带有标记的原子引用类“AtomicStampedReference”来解决这个问题,它可以通过控制变量值的版本来保证 CAS 的正确性。大部分情况下 ABA 问题不会影响程序并发的正确性,如果需要解决 ABA 问题,改用传统的互斥同步可能会比原子类更高效。 +ABA :如果一个变量初次读取的时候是 A 值,它的值被改成了 B,后来又被改回为 A,那 CAS 操作就会误认为它从来没有被改变过。 + +J.U.C 包提供了一个带有标记的原子引用类 AtomicStampedReference 来解决这个问题,它可以通过控制变量值的版本来保证 CAS 的正确性。大部分情况下 ABA 问题不会影响程序并发的正确性,如果需要解决 ABA 问题,改用传统的互斥同步可能会比原子类更高效。 ### 3. 无同步方案 @@ -1435,9 +1438,9 @@ ABA :如果一个变量初次读取的时候是 A 值,它的值被改成了 **(一)可重入代码(Reentrant Code)** -这种代码也叫做纯代码(Pure Code),可以在代码执行的任何时刻中断它,转而去执行另外一段代码(包括递归调用它本身),而在控制权返回后,原来的程序不会出现任何错误。相对线程安全来说,可重入性是更基本的特性,它可以保证线程安全,即所有的可重入的代码都是线程安全的,但是并非所有的线程安全的代码都是可重入的。 +这种代码也叫做纯代码(Pure Code),可以在代码执行的任何时刻中断它,转而去执行另外一段代码(包括递归调用它本身),而在控制权返回后,原来的程序不会出现任何错误。 -可重入代码有一些共同的特征,例如不依赖存储在堆上的数据和公用的系统资源、用到的状态量都由参数中传入、不调用非可重入的方法等。我们可以通过一个简单的原则来判断代码是否具备可重入性:如果一个方法,它的返回结果是可以预测的,只要输入了相同的数据,就都能返回相同的结果,那它就满足可重入性的要求,当然也就是线程安全的。 +可重入代码有一些共同的特征,例如不依赖存储在堆上的数据和公用的系统资源、用到的状态量都由参数中传入、不调用非可重入的方法等。 **(二)栈封闭** diff --git a/notes/Java 虚拟机.md b/notes/Java 虚拟机.md index 1d2b4f14..552a5e94 100644 --- a/notes/Java 虚拟机.md +++ b/notes/Java 虚拟机.md @@ -174,7 +174,7 @@ obj = null; // 使对象只被软引用关联 **(三)弱引用** -被弱引用关联的对象一定会被垃圾收集器回收,也就是说它只能存活到下一次垃圾收集发生之前。 +被弱引用关联的对象一定会被垃圾收集器回收,也就是说它只能存活到下一次垃圾收集。 使用 WeakReference 类来实现弱引用。 @@ -388,11 +388,11 @@ CMS(Concurrent Mark Sweep),Mark Sweep 指的是标记 - 清除算法。 G1(Garbage-First),它是一款面向服务端应用的垃圾收集器,在多 CPU 和大内存的场景下有很好的性能。HotSpot 开发团队赋予它的使命是未来可以替换掉 CMS 收集器。 -Java 堆被分为新生代、老年代和永久代,其它收集器进行收集的范围都是整个新生代或者老生代,而 G1 可以直接对新生代和永久代一起回收。 +Java 堆被分为新生代、老年代和永久代,其它收集器进行收集的范围都是整个新生代或者老年代,而 G1 可以直接对新生代和老年代一起回收。

-G1 把堆划分成多个大小相等的独立区域(Region),新生代和永久代不再物理隔离。 +G1 把堆划分成多个大小相等的独立区域(Region),新生代和老年代不再物理隔离。

@@ -457,15 +457,17 @@ G1 把堆划分成多个大小相等的独立区域(Region),新生代和 (四)动态对象年龄判定 -虚拟机并不是永远地要求对象的年龄必须达到 MaxTenuringThreshold 才能晋升老年代,如果在 Survivor 区中相同年龄所有对象大小的总和大于 Survivor 空间的一半,则年龄大于或等于该年龄的对象可以直接进入老年代,无需等到 MaxTenuringThreshold 中要求的年龄。 +虚拟机并不是永远地要求对象的年龄必须达到 MaxTenuringThreshold 才能晋升老年代,如果在 Survivor 中相同年龄所有对象大小的总和大于 Survivor 空间的一半,则年龄大于或等于该年龄的对象可以直接进入老年代,无需等到 MaxTenuringThreshold 中要求的年龄。 (五)空间分配担保 -在发生 Minor GC 之前,虚拟机先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果条件成立的话,那么 Minor GC 可以确认是安全的;如果不成立的话虚拟机会查看 HandlePromotionFailure 设置值是否允许担保失败,如果允许那么就会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试着进行一次 Minor GC,尽管这次 Minor GC 是有风险的;如果小于,或者 HandlePromotionFailure 设置不允许冒险,那这时也要改为进行一次 Full GC。 +在发生 Minor GC 之前,虚拟机先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果条件成立的话,那么 Minor GC 可以确认是安全的。 + +如果不成立的话虚拟机会查看 HandlePromotionFailure 设置值是否允许担保失败,如果允许那么就会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试着进行一次 Minor GC;如果小于,或者 HandlePromotionFailure 设置不允许冒险,那么就要进行一次 Full GC。 ### 3. Full GC 的触发条件 -对于 Minor GC,其触发条件非常简单,当 Eden 区空间满时,就将触发一次 Minor GC。而 Full GC 则相对复杂,有以下条件: +对于 Minor GC,其触发条件非常简单,当 Eden 空间满时,就将触发一次 Minor GC。而 Full GC 则相对复杂,有以下条件: (一)调用 System.gc() @@ -483,11 +485,15 @@ G1 把堆划分成多个大小相等的独立区域(Region),新生代和 (四)JDK 1.7 及以前的永久代空间不足 -在 JDK 1.7 及以前,HotSpot 虚拟机中的方法区是用永久代实现的,永久代中存放的为一些 Class 的信息、常量、静态变量等数据,当系统中要加载的类、反射的类和调用的方法较多时,永久代可能会被占满,在未配置为采用 CMS GC 的情况下也会执行 Full GC。如果经过 Full GC 仍然回收不了,那么虚拟机会抛出 java.lang.OutOfMemoryError。为避免以上原因引起的 Full GC,可采用的方法为增大永久代空间或转为使用 CMS GC。 +在 JDK 1.7 及以前,HotSpot 虚拟机中的方法区是用永久代实现的,永久代中存放的为一些 Class 的信息、常量、静态变量等数据。 + +当系统中要加载的类、反射的类和调用的方法较多时,永久代可能会被占满,在未配置为采用 CMS GC 的情况下也会执行 Full GC。如果经过 Full GC 仍然回收不了,那么虚拟机会抛出 java.lang.OutOfMemoryError。 + +为避免以上原因引起的 Full GC,可采用的方法为增大永久代空间或转为使用 CMS GC。 (五)Concurrent Mode Failure -执行 CMS GC 的过程中同时有对象要放入老年代,而此时老年代空间不足(有时候“空间不足”是指 CMS GC 当前的浮动垃圾过多导致暂时性的空间不足),便会报 Concurrent Mode Failure 错误,并触发 Full GC。 +执行 CMS GC 的过程中同时有对象要放入老年代,而此时老年代空间不足(可能是 GC 过程中浮动垃圾过多导致暂时性的空间不足),便会报 Concurrent Mode Failure 错误,并触发 Full GC。 # 三、类加载机制 diff --git a/notes/MySQL.md b/notes/MySQL.md index 5348018d..d49fc952 100644 --- a/notes/MySQL.md +++ b/notes/MySQL.md @@ -379,7 +379,7 @@ SELECT * FROM post WHERE post.id IN (123,456,567,9098,8904); 垂直切分是将一张表按列切分成多个表,通常是按照列的关系密集程度进行切分,也可以利用垂直切分将经常被使用的列和不经常被使用的列切分到不同的表中。 -在数据库的层面使用垂直切分将按数据库中表的密集程度部署到不同的库中,例如将原来的电商数据库垂直切分成商品数据库 payDB、用户数据库 userBD 等。 +在数据库的层面使用垂直切分将按数据库中表的密集程度部署到不同的库中,例如将原来的电商数据库垂直切分成商品数据库 payDB、用户数据库 userDB 等。 ## Sharding 策略 diff --git a/notes/数据库系统原理.md b/notes/数据库系统原理.md index 1d493176..87d210a9 100644 --- a/notes/数据库系统原理.md +++ b/notes/数据库系统原理.md @@ -346,7 +346,7 @@ InnoDB 的 MVCC 使用到的快照存储在 Undo 日志中,该日志通过回 ### 4. UPDATE -将当前系统版本号作为更新后的数据行快照的创建版本号,同时将当前系统版本号作为更新前的数据行快照的删除版本号。可以理解为先执行 DELETE 后执行 INSERT。 +将当前系统版本号作为更新前的数据行快照的删除版本号,并将当前系统版本号作为更新后的数据行快照的创建版本号。可以理解为先执行 DELETE 后执行 INSERT。 ## 快照读与当前读 @@ -491,7 +491,7 @@ Sname, Sdept 和 Mname 都部分依赖于键码,当一个学生选修了多门 有以下函数依赖: -- Sno -> Sname, Sdept, Mname +- Sno -> Sname, Sdept - Sdept -> Mname 关系-2 diff --git a/notes/计算机网络.md b/notes/计算机网络.md index 1c38cc0a..8603393c 100644 --- a/notes/计算机网络.md +++ b/notes/计算机网络.md @@ -664,7 +664,7 @@ BGP 只能寻找一条比较好的路由,而不是最佳路由。 发送窗口内的字节都允许被发送,接收窗口内的字节都允许被接收。如果发送窗口左部的字节已经发送并且收到了确认,那么就将发送窗口向右滑动一定距离,直到左部第一个字节不是已发送并且已确认的状态;接收窗口的滑动类似,接收窗口左部字节已经发送确认并交付主机,就向右滑动接收窗口。 -接收窗口只会对窗口内最后一个按序到达的字节进行确认,例如接收窗口已经收到的字节为 {31, 34, 35},其中 {31} 按序到达,而 {32, 33} 就不是,因此只对字节 31 进行确认。发送方得到一个字节的确认之后,就知道这个字节之前的所有字节都已经被接收。 +接收窗口只会对窗口内最后一个按序到达的字节进行确认,例如接收窗口已经收到的字节为 {31, 34, 35},其中 {31} 按序到达,而 {34, 35} 就不是,因此只对字节 31 进行确认。发送方得到一个字节的确认之后,就知道这个字节之前的所有字节都已经被接收。 ## TCP 可靠传输 From c0c06138212dad1641c1bf720e2ebff1e90b62e2 Mon Sep 17 00:00:00 2001 From: CyC2018 <1029579233@qq.com> Date: Thu, 19 Jul 2018 14:00:56 +0800 Subject: [PATCH 22/26] auto commit --- notes/HTTP.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/notes/HTTP.md b/notes/HTTP.md index e6a823cc..915e19ee 100644 --- a/notes/HTTP.md +++ b/notes/HTTP.md @@ -66,7 +66,7 @@ ## URL -- URI(Uniform Resource Indentifier,统一资源标识符) +- URI(Uniform Resource Identifier,统一资源标识符) - URL(Uniform Resource Locator,统一资源定位符) - URN(Uniform Resource Name,统一资源名称),例如 urn:isbn:0-486-27557-4。 @@ -232,7 +232,7 @@ CONNECT www.example.com:443 HTTP/1.1 - **500 Internal Server Error** :服务器正在执行请求时发生错误。 -- **503 Service Unavilable** :服务器暂时处于超负载或正在进行停机维护,现在无法处理请求。 +- **503 Service Unavailable** :服务器暂时处于超负载或正在进行停机维护,现在无法处理请求。 # 四、HTTP 首部 From 466de93070a03aa405da8867a732f0fcc9286348 Mon Sep 17 00:00:00 2001 From: CyC2018 <1029579233@qq.com> Date: Thu, 19 Jul 2018 14:08:12 +0800 Subject: [PATCH 23/26] auto commit --- notes/代码风格规范.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notes/代码风格规范.md b/notes/代码风格规范.md index b1ce8137..dedada7d 100644 --- a/notes/代码风格规范.md +++ b/notes/代码风格规范.md @@ -2,5 +2,5 @@ -- [Twitter Java Style Guide](https://github.com/twitter/commons/blob/master/src/java/com/twitter/common/styleguide.mde) +- [Twitter Java Style Guide](https://github.com/twitter/commons/blob/master/src/java/com/twitter/common/styleguide.md) - [Google Java Style Guide](http://google.github.io/styleguide/javaguide.html) From c66d151b47f8b57f8b586c60ba0aa3ca1b457c31 Mon Sep 17 00:00:00 2001 From: CyC2018 <1029579233@qq.com> Date: Thu, 19 Jul 2018 16:15:43 +0800 Subject: [PATCH 24/26] auto commit --- notes/代码风格规范.md | 2 +- notes/剑指 offer 题解.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/notes/代码风格规范.md b/notes/代码风格规范.md index b1ce8137..dedada7d 100644 --- a/notes/代码风格规范.md +++ b/notes/代码风格规范.md @@ -2,5 +2,5 @@ -- [Twitter Java Style Guide](https://github.com/twitter/commons/blob/master/src/java/com/twitter/common/styleguide.mde) +- [Twitter Java Style Guide](https://github.com/twitter/commons/blob/master/src/java/com/twitter/common/styleguide.md) - [Google Java Style Guide](http://google.github.io/styleguide/javaguide.html) diff --git a/notes/剑指 offer 题解.md b/notes/剑指 offer 题解.md index c0c5412f..20963e46 100644 --- a/notes/剑指 offer 题解.md +++ b/notes/剑指 offer 题解.md @@ -804,7 +804,7 @@ public int NumberOf1(int n) { ### n&(n-1) -O(logM) 时间复杂度解法,其中 M 表示 1 的个数。 +O(M) 时间复杂度解法,其中 M 表示 1 的个数。 该位运算是去除 n 的位级表示中最低的那一位。 From 2c0517e54f43ce45450c499cd5a9ad308512f323 Mon Sep 17 00:00:00 2001 From: CyC2018 <1029579233@qq.com> Date: Thu, 19 Jul 2018 17:15:21 +0800 Subject: [PATCH 25/26] auto commit --- notes/剑指 offer 题解.md | 95 +++++++++++++++++++++--------------- 1 file changed, 56 insertions(+), 39 deletions(-) diff --git a/notes/剑指 offer 题解.md b/notes/剑指 offer 题解.md index 20963e46..7ff73846 100644 --- a/notes/剑指 offer 题解.md +++ b/notes/剑指 offer 题解.md @@ -440,7 +440,7 @@ public int pop() throws Exception { 如果使用递归求解,会重复计算一些子问题。例如,计算 f(10) 需要计算 f(9) 和 f(8),计算 f(9) 需要计算 f(8) 和 f(7),可以看到 f(8) 被重复计算了。 -

+

递归方法是将一个问题划分成多个子问题求解,动态规划也是如此,但是动态规划会把子问题的解缓存起来,避免重复求解子问题。 @@ -603,13 +603,15 @@ public int RectCover(int n) { ## 题目描述 -把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。例如数组 {3, 4, 5, 1, 2} 为 {1, 2, 3, 4, 5} 的一个旋转,该数组的最小值为 1。NOTE:给出的所有元素都大于 0,若数组大小为 0,请返回 0。 +把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。 + +例如数组 {3, 4, 5, 1, 2} 为 {1, 2, 3, 4, 5} 的一个旋转,该数组的最小值为 1。NOTE:给出的所有元素都大于 0,若数组大小为 0,请返回 0。 ## 解题思路 当 nums[m] <= nums[h] 的情况下,说明解在 [l, m] 之间,此时令 h = m;否则解在 [m + 1, h] 之间,令 l = m + 1。 -因为 h 的赋值表达式为 h = m,因此循环体的循环条件应该为 l < h,详细解释请见 [Leetcode 题解](https://github.com/CyC2018/Interview-Notebook/blob/master/notes/Leetcode%20%E9%A2%98%E8%A7%A3.md#%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE)。 +因为 h 的赋值表达式为 h = m,因此循环体的循环条件应该为 l < h,详细解释请见 [Leetcode 题解](https://github.com/CyC2018/Interview-Notebook/blob/master/notes/Leetcode%20%E9%A2%98%E8%A7%A3.md#%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE) 二分查找部分。 复杂度:O(logN) + O(1) @@ -732,10 +734,10 @@ private void initDigitSum() { n /= 10; } } - digitSum = new int[rows][cols]; + this.digitSum = new int[rows][cols]; for (int i = 0; i < this.rows; i++) for (int j = 0; j < this.cols; j++) - digitSum[i][j] = digitSumOne[i] + digitSumOne[j]; + this.digitSum[i][j] = digitSumOne[i] + digitSumOne[j]; } ``` @@ -747,24 +749,17 @@ private void initDigitSum() { 把一根绳子剪成多段,并且使得每段的长度乘积最大。 -For example, given n = 2, return 1 (2 = 1 + 1); given n = 10, return 36 (10 = 3 + 3 + 4). +```html +n = 2 +return 1 (2 = 1 + 1) + +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; j++) - dp[i] = Math.max(dp[i], Math.max(j * (i - j), dp[j] * (i - j))); - return dp[n]; -} -``` - -### 贪心解法 +### 贪心 尽可能多剪长度为 3 的绳子,并且不允许有长度为 1 的绳子出现,如果出现了,就从已经切好长度为 3 的绳子中拿出一段与长度为 1 的绳子重新组合,把它们切成两段长度为 2 的绳子。 @@ -786,6 +781,19 @@ public int integerBreak(int n) { } ``` +### 动态规划 + +```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; j++) + dp[i] = Math.max(dp[i], Math.max(j * (i - j), dp[j] * (i - j))); + return dp[n]; +} +``` + # 15. 二进制中 1 的个数 [NowCoder](https://www.nowcoder.com/practice/8ee967e43c2c4ec193b040ea7fbb10b8?tpId=13&tqId=11164&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking) @@ -965,28 +973,19 @@ public ListNode deleteDuplication(ListNode pHead) { ## 题目描述 -请实现一个函数用来匹配包括 '.' 和 '\*' 的正则表达式。模式中的字符 '.' 表示任意一个字符,而 '\*' 表示它前面的字符可以出现任意次(包含 0 次)。在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串 "aaa" 与模式 "a.a" 和 "ab\*ac\*a" 匹配,但是与 "aa.a" 和 "ab\*a" 均不匹配。 +请实现一个函数用来匹配包括 '.' 和 '\*' 的正则表达式。模式中的字符 '.' 表示任意一个字符,而 '\*' 表示它前面的字符可以出现任意次(包含 0 次)。 + +在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串 "aaa" 与模式 "a.a" 和 "ab\*ac\*a" 匹配,但是与 "aa.a" 和 "ab\*a" 均不匹配。 ## 解题思路 应该注意到,'.' 是用来当做一个任意字符,而 '\*' 是用来重复前面的字符。这两个的作用不同,不能把 '.' 的作用和 '\*' 进行类比,从而把它当成重复前面字符一次。 -```html -if p.charAt(j) == s.charAt(i) : then dp[i][j] = dp[i-1][j-1]; -if p.charAt(j) == '.' : then dp[i][j] = dp[i-1][j-1]; -if p.charAt(j) == '*' : - if p.charAt(j-1) != s.charAt(i) : then dp[i][j] = dp[i][j-2] // a* only counts as empty - if p.charAt(j-1) == s.charAt(i) or - p.charAt(i-1) == '.' : - then 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 public boolean match(char[] str, char[] pattern) { int m = str.length, n = pattern.length; boolean[][] dp = new boolean[m + 1][n + 1]; + dp[0][0] = true; for (int i = 1; i <= n; i++) if (pattern[i - 1] == '*') @@ -997,10 +996,13 @@ public boolean match(char[] str, char[] pattern) { if (str[i - 1] == pattern[j - 1] || pattern[j - 1] == '.') dp[i][j] = dp[i - 1][j - 1]; else if (pattern[j - 1] == '*') - if (pattern[j - 2] == str[i - 1] || pattern[j - 2] == '.') - dp[i][j] = dp[i][j - 1] || dp[i][j - 2] || dp[i - 1][j]; - else - dp[i][j] = dp[i][j - 2]; + if (pattern[j - 2] == str[i - 1] || pattern[j - 2] == '.') { + dp[i][j] |= dp[i][j - 1]; // a* counts as single a + dp[i][j] |= dp[i - 1][j]; // a* counts as multiple a + dp[i][j] |= dp[i][j - 2]; // a* counts as empty + } else + dp[i][j] = dp[i][j - 2]; // a* only counts as empty + return dp[m][n]; } ``` @@ -1011,10 +1013,25 @@ public boolean match(char[] str, char[] pattern) { ## 题目描述 -请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串 "+100","5e2","-123","3.1416" 和 "-1E-16" 都表示数值。 但是 "12e","1a3.14","1.2.3","+-5" 和 "12e+4.3" 都不是。 +请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。 + +例如,字符串 "+100","5e2","-123","3.1416" 和 "-1E-16" 都表示数值。但是 "12e","1a3.14","1.2.3","+-5" 和 "12e+4.3" 都不是。 ## 解题思路 +使用正则表达式进行匹配。 + +```html +[] : 字符集合 +() : 分组,在这里是为了让表达式更清晰 +? : 重复 0 ~ 1 ++ : 重复 1 ~ n +* : 重复 0 ~ n +. : 任意字符 +\\. : 转义后的 . +\\d : 任意数字 +``` + ```java public boolean isNumeric(char[] str) { if (str == null) @@ -1029,7 +1046,7 @@ public boolean isNumeric(char[] str) { ## 题目描述 -保证奇数和奇数,偶数和偶数之间的相对位置不变,这和书本不太一样。 +需要保证奇数和奇数,偶数和偶数之间的相对位置不变,这和书本不太一样。 ## 解题思路 From 06c87bf312d3a995fe3d94f9f22908a0e4325356 Mon Sep 17 00:00:00 2001 From: CyC2018 <1029579233@qq.com> Date: Thu, 19 Jul 2018 17:20:41 +0800 Subject: [PATCH 26/26] auto commit --- pics/a0df8edc-581b-4977-95c2-d7025795b899.png | Bin 0 -> 18432 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 pics/a0df8edc-581b-4977-95c2-d7025795b899.png diff --git a/pics/a0df8edc-581b-4977-95c2-d7025795b899.png b/pics/a0df8edc-581b-4977-95c2-d7025795b899.png new file mode 100644 index 0000000000000000000000000000000000000000..9103dfa6dd84bac59f44c9865e9039516bd979b6 GIT binary patch literal 18432 zcmdVCby!v1*EXu6AYIZWQc9;ZJanVd(jlE2kd8;X5s_{wiA|SuY(P4tVJqE8H=Mcg z`91IVz3+Ff^XEC|4=%2~)?9PVImVcCj&a{(tkBoWGMH$jX!q{j!<3ViRJnKWemVGa zevAyRK#x&sz`y%WDl+2tN(RX{z>kOKVoGB7?v+QQUl~0Dzfm1z-#Xp9ht+}jyWeg9 z_1(RDGRSh0Vrp*sJ87Z5N-|5gzPBnZ(2%Fk@EB|8#YwGS_>JSe!DEc1%gv3SN(g$Q z{zU9q)z@cYHr`be+UYgv2ew7mCzjTe=jV!pNrIV0#NeUH|2H1`Na%EX;|*CGq<|c6s=v|HWWb^jpmo`ud(qq53%fYp>WrcgT&^C= ze-&;$SrXddx)+9mO{tYZPwR{rJKJ)Cfe)^_y84%|UuARh^1^PmmaBRYJ@{CaR;;Fv3Lu&2P-sZ%aHhEMDEz%}~Oksce{F&!=m3=+1Ki4Hkf?&-b9UhUl(#F%Zw_qMO^ zk}w!O zn2pfmD7nz^aMif0s-*Bpv!7-JPrSb_2G`vjjlI&?mSYwJ!}MQXTPs5IIjVb*jasK? zUMh-D)7=bRJU^MW*C}Txe+Voq8gjDzR=u1d-x^?{b>783a}@* z1xSSx7K90T{+2Z{-801`=9KSjsj3>6Cs=%sxP6a&qm&9-vDRnZ6}Hef<9${bCOYcp z$<&367(#0jm$}-b^by~(@=wu^OpnNNM(^_R zfnW3^Bcoa$-M%Kz(_*z}`e4Hf>6>oytSzi6$BZp-jHcT_vDlq&n3ShmR0o5uBPYkQ z)SOY6*yYsNF3;&K@IJ*PnqGLjubOkXO=%rtedW%S()7N9 zkIsoapQnww-nzZoHJZ(jTe*}}aV%U<;gKZ2zTj=StWnmz(($UhQsprG{d}bM){w*J zK=BRa_b1X0LB|uKHwQ*ZSLsOAqA$<9oC3MamSR4Ce)pe|Vh|o}u8xh$QMmb>pjiJ{ zGcx0nj}Jc>iYhQSbey`Mqm;r|ZglzP{q^_?%3}5_Y=xx?QFvnLj-ZPIX~K0Xw612~ z`0X?$36{l8#jEAx+Ji93oz520#(e(vWvpw#I_k1ogsO~p7n+I~w_wUWUGhyYet2Ex z6N@mu@}Wczj847$u_qE?I_MX@e9f_x9~zsm+qcEeINxZKq==yx=dP--d>s`kSHV-e zxnvbH!KJ6a^&=mC7(a8r#+!D4==8AMATw6BTW`Z_uAnXZbfqYh=u8 z2gFPpO*XLYY_W4CCVMy0ZB7fSHeL3{G5y;yxuA=p`h@=iv2F1%8NRkasp-2uR$(tn zC~Z|2GWHO`rFC<{P^+#3+wkjQzYf-O;%>kFwtM@jJ+SR*KlVmj^|kT$ij*6lX7m<3 zJugre`(1(bx*{s+MtPA+pwMcSa5r3F!>rNF0T4dUwOsFV9bG? z;SmHLChZul)hR8nO;i(kLPE)OPSOdhhjg^-dzkGP1J?5t68Diw5lrQ)qEsH)jK4yva<58STg-;n6~1RiO039ABz{ELK)^} zE7q#uYtB5Aum^h^a0<5}J33^2`x3MTz2~CXBd|tHuo{W{ZR56k1$ga)%Ej&F>tg~s zV4EdEpYcTb91EMg!G!Ffp;^`h8NqeOpZ}PYm6ccb0X9jxb@6bDN}Uo$MK_E+ zR059ra6U=c<23H{D3s#0wUVMi<3K3ILJ(Nd;UoD|`Q}*M8tY00FYr6g2)fE0?7?}_ zGPL0l6KW(hfR){Rg5DKE5>kgXHY#c|WPw4>WLYbH|S5un>Src`P-2@HwEkNm#rZd ztK-GdMmFP4R|LJ5kx*|sE$);tnzYtmn6Vw6NCW8YJf0pxCptF8$CIB=D4GgJ{bkDC zs1UZApPBiW`b?3?f_?FnPIjcA)w@1f_*Y)t_7P@_4@MgA%FL6Y%%?j&Ayw3Ov+irF zk$1^WJDgB6>0BVqdVjR*$+U8r-^c}@@UbkI%)-LLe|P8e zom#kB?tHVv7d~$mA^Ig`>fQbQ9W{@u^R)~OM%+@w@<+lTQ4!@wnyD(<=h4$Aa+#MQg7--^n? z0r7r!R6Q5Tbu$F>?oeA^ey9Gyw`v8POQ#h$=7Kk}=y+tnI`$uXBp9EjZ!9WeDEBSWrA+wY(XVg}(yu$U&OOBp(pnf67iS93X0)rV;^Lmc zwh)8H`PzuYbTo+)iSu5QicG0>Ks>bq8AEg5>-dM}4|sW{sE$ebeS}-_8aU1RKIf+D zFIre{*OiUbn^4_7al0f%Hr_aFMK|gm-m`w7t7sZjL7>* z&tkAQTTPWL>{%1R)S75ms^2u;bbh#|;~JuQXSWZ!+g$b*Z08Gj>)PAJ-|xj~>c^xk zC-=8u&NN&vD%NQ-nj^<)_aT!fJ}xdd-Nd^8?%aNn2Ps3u2d>xdS7+tK{Ej&u zguN*O3}2USX;_%e()mnl45nnFDOu8m+>JX+sDrUDPu@scZ=dYUPEaiJApD3Y-sH@z ztjdx8JY8_#TNWL*6Z6dUCN8NTBPr5{4t_W_Rp}c(M$+V@T-Fp#JZ4r|c=iYb2((#f zoGvx)Zb|7g&+w+7sX1oBktWjHn9-BQ56vTlit{b0^UYRUGjuHI9!(H;jZBdqBAiRz zu3EScla9X2qqKws^La~=$x;KNjg1?Y)c(ofuw$KQVoW*}0nh6s3<;0bzgo(ui)-!X zE5zYm3topx$5=I8ji?BTeb9UEakQSrXm%w$mG)woPE%L6c)=81FCwwaN5D_!o8f8X zim4}=Glh7f`0ejs5=`SPOGg`Ho5B{6AXplxmuP%|=q4s3qm|VvlQqBZO(U_O zOR+~OblgrD=C_g&7Mnmt9LexR0J71X*m8v5f}do&vbq|EPQAI&p#l zbFDukvp(}}lyzMK2+>4lr<$W`hI=s*+A`J6eX(^+7C|L&6!_aPgpX;DiOnu^-7u6UXg*JE2LeX$R5za%yJ6VHV4C2H#w#uV zxYX2ba||Wap+&B#2k3isPHR!e6W>1&CQ?vF#NytWLECymg+vx5iZx3#rSPZKp!k6g zNGOu-?%$^BAlX-fCBG;^#Ksh4fa8D6CUe;V4L}FLgUbCLO&r;YzYG$KnB9RwF zeT#pU1u=1PsjAn-kfIz4SdeTs{G1#g*K!TfL9DF5${GUnMx>p{`@`2Q!yy8TcEEV~ zFs_A|LGapNFCl(k+hl5bLD^utTy_qD0HEOVVZF2(|B4(e@@c}Kt81Iv%({Obady=WXg;Lcu{Gi4c zC4c9YTrw~#*(X2w5-W$X$(59pOs)H+5IwV#qc~Brv($9Wfu->AaR-Fm9B+zrDX4E3 zL_@L<3yO+V3FxZc_26w`g}$_iG%^mpo^^;Ar9eE3!?#Oy+fmEZBj%6XQzErUel(E4 zVFpxi2!xDxpWTv%fuXz954Mc8$Xqs+1m$oAMoIf%-8v?dMkWdvE@-9OdvHjdaB87HTXOxh#y^_K8^DKmf^HESOCX| zmIEl|t!oHALe$ULu?UHX@);ZXomRngW_$YjR9l#Clv9y_EOBu7uqJBl4fcPwA#Jfj z1TDO$TUcNdiB5U5LPxj;bEz=sG z*7o0T^H27oc;3<01683uxPE9DvSe7^0aoj>e%Oiq(1eyVnBJ(k-CP|t*g@&KAZ1fX zo<=h*w<-3`mU?s}2>qkCCTg0Q6l@^VoUwlIzgtoz6z{g*S)^GAC=OHu#Q|7+k#lMY) z)-DnnurB0rB%70y^Hr#QLhBu3E|2+Q-tI9j4zA>I94TdvkouR$d`BEF_qy7Uu_=og z>7)E(@*dknmUmL}gT9D=@)29Un60(fbf@RxiG!9uMwrb#(8#bg0Lz(<6E2AW_ejCK zpkf9%(G$cCnH`8|JKXo?Jb1}4M;%C{j5s-f2(%)+*hL2o+bVo-ed3~r3)g%D+QH88 zac%s^k5GWKy;T<>A9@PCuk(7^Jrr908~Ae^L!~V@jNNS8^5)}T-%L=5`$=Sw2TTwj z5TjyqbKs2`k~1?f$f)yaZTVMzd2GqT$2>NusZ}A?4y=pM@A7mnT+nql1RzJfB~22+ zSfOcxuD#wH6Qu@hF56SorN5q^{e$`iEwjselVi+>3R$HGpe@r0+W)#Y@Vct&i@I_u zhEjxNpn=c1l$yGFj|Ba3Nn?B=c+9vfg5*Gt#a8SQu)J-*fMmq^bVZGufzDvAfS8la5~E!j0S8b0PGiCt~U9_b$qEiA43EmvTept)8XWk4Ko z&xD2NwGwTzq&_EpW*+r04Y`@_fc;Jt;$UsjQp`|iL<=Vh-;Xu#wSJ2f)Mt(-NOxKN zMX~q$_m8)SUKd9N1f*0z#xc)$c{Nw65o=EK+ER{RPFem5Nz)jt?8CL>LZZ78&oHqH~ZO<}q4oHM%lN}e4EsSZI5s>1^P(FYZ(UrX#9&$LWTX1Y$^DK__ zR)Rp(1hU&D%AztKCv#Bg*8Mj%|3O;bUi+eIz$1c3!>;5q_t*G{Q5lF+Dh_ACzVXZs z-fTPX4&j%xAYzloCVM);n)+hUyIe}>{8tW+XqsgVrLV7w58>W-_Z!y6u_L_?D=-!y z581Y-oQe;;e}4#k2+dz~FM`z+9*QMRga^3VK?N9Ekl+Mo*Yox7@yTA=eDs}} zu{>910s|g3mY0`T3$h!_i#&Vhnl?f^mpRbzs)uiCKZ(~qoj^fcJJ^@sT`F~PT}E^e^^=mdBP*@HKkBkn8-vrVktcJ{6+0%ERs#_7 z7f^zuHtGb$A5l8I?y_x&1YSh-3y!s z?(nNCuQ-TIG!uLB-?Q&eF1R=xw`UpC?OU#E2t37;gY}is7*>guT6ZqiMDsg8Lz+nv zMi5#%5FbnzF5w@_FR8r^uZ-zTvOePO}EZ4_W`w1c0#OOL62j!cr2=$m{#Vs z)g)`wbA<3{6Xn6KZ4mc1A#R69<3@k9BKqX(nVGq{(~Ph?^@o*rdwIECy>Nw@%JOmC zl>mC_TeF&6Ty|Pwl z6Y(xm(~&PyjQ(cWl$OQYO~v;mqzldx;^Vao#6v_q_(^!UXSJq1-}2kPlc@$T2*D*+ zFQFnS{~vJir@tQb^|)3OAVB1L4OQG92<=D@rt)WVnn~Zbb;*kmejc&^D38)FLL(`| z#^7%-#IA8ro+XjNkQqqGMDgjAOn-i%Fc~3!Js;oeSaw0>*)03HCs9fFiFP zE`(|WE5W6C`}QrPs@<&X0s=g!(YTnnF(l=n7VzLdzZljqc|?USaM~IFH4b5et$QuE zC7karu|gdg&{e^$_IA7Lx_Hmi-M0mj#uQDdv@SMmt`+R+-~|k#sYik(bf4}-Q|5VU z7f15nA!xj<^_7RS>`9ELS}H!fDQ{jNbbOE8!`!@!&f1lP=|pRE@keuD@mQWxrAjCa z3~9RBsJz@bnr`R$Aq!8)JA{=ZzkB!YYk%G0p?xHQXPE$G-+q52USe_nR3mKqa57XP ze~&c7>C;DK7mug5kMDc%e>?kuWztEhAWkZojZid_!28V{&ZRrhT9{bf=e^$1=Gdzy zvKfH))kl}tV$Md(`|C8EqeCWU7B|P^f{l;6u8r4ImL%af{+%Hlv7M`7u|7J6>QuM2 z&(mw=8*456`7Vy_H$9#QaF=zY++ozA?~fcEPuq#Q!i2Sqymk)C(~MWgAq8=hmZ zB`RgojQ3$5kw*|lzKBA|I75Pq_9F+2c;u{yYejIjDiojJo*)DOqf(u13Y#Rvs@*W! zeQoI)zIRD$F-|REzp@Xm+TWp@xvn-a9PnT9ucTcdT^?;^;n2IT!}~Bl$QkmZuH;}f zTAy|+Nb2g69p6~GXLtJRRB!tHST!A;tAXleo>(^eR!VzSxy8%MZ$B5shrd*&L~**D z^KqvdiepJ)5e*&+_IQTovQyiEE>(!IU@F(W1@$EMa@C4*6>RDCmtIi&>l1Evc4wx| z(`cXT24W$b;zk3j$d0y#H3xj^{-b6Db7R z7V|2fq+HCXemuCk?1H>Gaw$Pm-J_h(3H`OV(NrJ4#c=X{%|}Rwh4=aHzExp8q5S(@ zHT%rhIfxzX46-pw7l&FN+fX2mEv;@ak5e%3@%{A6BSJUdAKk|?^(Mg)9r?GK`eTvU zP`;{F2Z|tGL)oZC5wk09$ib+$I|a0BnyT3Lx7YLWO4mFuHs_UI0;X!&;x7sK+sk%^ z!~O&q==A=snxEPB8&20R@hiePelH{;Wqge%qfg3=dlXv%uXNsZ{&66_g*hpo_v-Ur zB^(#cv{}QTD@zu!7pi`$$`b=Bd%^naZACJW5dbo4?%f|?Et@u1KRfPW`dL=%d!1oTpQ}X7*l3I-mv)1bBY7MJ9*_s4l z0|dEky>zR)vjxkZU>=IZP+NTS*Okvo$fi8ZZxd^0wnt;?)@O+<*gxx+dm^DC&g5?r zXNg4tZOiE9QYuvmYYZq?)oMwgTd9mU*Uvu#mN2;d^atoy$yzaOQPqSOQ#;~y0aZ&y zFL>wnY~Tw!<9nK)sF>QVn|T4s;6Pw^iT{!df#+Q`-2^q7yD9{U1I#~#S?%px%Ur@x z^&>}48sFk_prXW9qZZAC`OWr3qMPfshFJB}XAimjUbOw(&NdwW@wjqK6x+z}`*u6( z!39Ing65<|)<$gCS9X~{_G)Ts%837^+N5w;r?5$;IX39iIKNc0>rtI}x#c8kIdn61 z`@kRa%cFL;sk7Onc6`lQqFrn9WV1AbY)X}%H3-6{i_KkLUlcu_NYYMK82SRi8@P(E ztPC-s;*G7ZyaSG>Ng!p!OJ)|_Yc7w1k~V!yy%os_wi;+WJ}(;mxNEkio%ZS}zcaz^ ze4Sd9zmc)b{%6Rh@VdO$nfqY zyZJz%tMzE;uf#yUb0{vOE)G2;)0EI{lv^bF(v51N2yPuoIFJ__^W1;Ocv~?sg||a1 z3nkl75kq|PS4D;J7X>TVb4A@*H+~ab^xS=WXNfO@Dz}P!fYd5-V}L>PL-duy=+`dP zm!aw=^e1~`>W&lC^%xu`Z*DPe(q?j-T_5NLvX>B0^?zjHs_J4lHONq*V#Sx3EKK`n zhB8W#ds;Wt9Zr!19_(lnFqk(eWb=j$-N}cOO09m*n(juWp0+%Kg_@mZ35J zpk*0?>lh6d5!R<#N@l;n+=uT)^=`34--mB@53`$6sh;i(1@yrjWiaCR0>OilkGwY> z02+wU5MU;XkK5lXmUb_iU8l>Pc(8jRvv6Lgi#Y8=8=t))`eJ$F&FNKHf}uT-jj8s3fYyXP3Udq3>; zr)cAiHwUkaz>+IKSSvn(=C2VyS-bl*2b20QHik$Nc|H)_*JH?Fxa(bc?3e8)bp>b; zza;2dV4bvUl(xFWsdL1*@BHBM7Rz?bNv+2p?3ywZOm{36hV6 z2W+z5Wy7ZUr-@jIXVu*t`h<(* z0|=Kn@{4Pn^Ij6`AZ+*{HjF3NIvJ6UfrIbC-xk_4kjAq9h`Pjbesc&lLIdGON`Ldc zZPs6w5w_mYzx zrPm$RmXDr(a_~~PW4LN2A_iE=@VkLV6xdb_lQ z4@)rj~v-_#w-Tkn{S-IJ5xnNPT6zna)}_h3C8*wS5L*H4w<3sEPUFbgz_i zd&^%s1e{%w^gqq0mER(5@@fVmV-Oo6g1ao>uGpR}qwMW0?k@+&8lOEi5XweqCg*Lc zzQod$`*dX+`IOzF-w7gfs8aQ2fa*=vAVJR zbz&*58J{3!Oa4wDpS>>@*k{dibo{bu0&|FEuW|KB)VDD2n7D9;Yn+Xhe_<*9!D14 z2yYHG3_uQFBd$jk*a})sx6@t}MlEnB2jd5n`U0ge zMVD%Hk>~0~ZQoS`@{-DAG8)blWAyLnH?;c0uN||H>m-$86KvH|a*)dBID?R%BJFN^Rc*RGuj6 z+iXBDQ~L$8o-`~x{CZbn-5ADwGKMSE`>4U5nQgYH9_zi4)$fjAZ0Kur_tbiC?8=hI zs0@*D_|6>0q?cn)K9dOQ$ZdgHyJ=X{j!qy{vqMum9Igf$z5t* zPak$f7d~2AowGr|!9^%Mw7zpxn!GE(hs=^y53G?_{*1tPfwQjGCaMIAKaBC>Dv+~e3lqL$Ae3}B0 zZJncy`D-Y5Rn70W5iP&;+{|ZdnmKHA23{~I14Go8HCZwoc=U+I!9^nzL0!iHD7E4C z!Wuqp3=tAAP%d=K9vt=rb@)`615`cug`XiZyD> z%|+4Ik`h8L^T8*Umd)9>5_G9+0j_`-e>K(Jmcq|-fKw4OYbxmZT9=7T)&5VMWmJ zi?ih>GW)akEpJ^PL$CngqzuT+2*5|s(b9&b@Y-v;HuV8EVwq6QrSaRnWE^t)b9F=+yDCCMg(w`qPHss(x zk&uRrqir65vj&KSFf9rcYYgxE++98F`qGafgxnloLRKK`_#eUb!j;ALxAdv8!bv1;0AL}sA}XzZFsh45l#Bd%szDnSg2i1~!xb$sT2eb`q1L zSX1yNKa#1M+UR~X93TnahtUQp8~(e4bBBBRfra=NbR}kKprG~E4*|j+N3Sh=5Y4c` zuv$U0yMrtKuD8t4&U@r2=-7a?P`3=D*h8fT%+D$Wh<6wh&``q;zuF(gmEhbcJq~5& z=D-FC4S;21VAXH5n5WBn%EtC8mrdI&;Ny33{6ls?zXD~}?0$TWTX&Dt#1vc>!#{iu zE?aPX{GAEpIbdZ?`nXjw|2BgK=SV5*2lG=}{O_Na)KKtI$mf%L1m zN5}GrW{61wLdyMKgy#kULwDO3bZx^E`!*mAprph;v2b)2wuaGVKCPoRE#jAtYJBlo z|E8z)8iwg&bho9VN12cK5+SY#{pSi+FYH6o$zBJd2bAPQgZ_mV&U z{tMrU8ql~XL+Qf!Nl8hYu8UxZ;$VoT4&?oMsGTf)?c#BW_>K%UCj*_B)j z{B{+WUC#{t_89P+_*KnEa7-wdxU2=Z0@lelvVV=&667Tl0Xj3NvYiiWmGuG9UnZ;1 zsB?}%XMc-e1?g=y13Ry!%|XGfDMynABH8jU05?(#Lm)D4hyVch2T z$VX2&OQXf-df9b+V&bJh&3i!CM)`N%v+&L$FuNUWWB%JkrJ0Ua8&(x6?0z7rrlApb zbmVOEt2%JEaXCjaW<*d4<9}|fI9&Nrv6DgAn`{|vm1hwfdNrJ!oXfY)86w_Rfzsbg zQW06q=3o-GU;$q1)V{m#tZ-m!tWqJn6-RwxE;^LK?{2K?Q=TYNr1pf6jc|!!=<_5< zZ%P=Vb&06+DV9T?!>R0^Q1hk zuH``DJk8p9!|#6+=LzuZKWuLS;#}mo;$5*xGM(nCmOir1*`Dg0D57=qit^Z7gtE=blR_qh#HxiMb0c==N&&2 zKL7QZ?{;qdH^o^=>s~i;S5awm;SET3uwojA^%;H&yiVb*xmRD{6+WT5->7b^^W!{I zaZ9RQuvQ~27XD!m?dryEzP_gnBK|m(ki*3Ij^x6wv6eS4cV7|O|E&QX@oLXP;k90N z&7);F4_wJj(8ohXMK%8Zk(%5y1Q(G1%e9}urs`~RUh{vZ#{MhC|D!BEKE2D)`Ws6u zkF2eQ>hP(0vu3xWJ$&ix(173bkC7tOuyq<-cHSk**^~k5_jq;-JyYrY<^LTgX4*^_ z(=|pPG|>;}wrey}?$YO?Usy_sP+2O!anbWUW@va~5}YLwTnMKSTu3>v{qHo`st%r< zkhQVadO>OD2bBieP}R$qq>(UG@hKc>;eL?r^$|k7u(~~6>C@6s^55$RlFs8L(OxAJ zn<*kn2TEVBtYHz|UhjVW-%1DG8gjIb7pO{Ip6p!vT=L4i!s}+cGUCQ&KsA!oIxgda zf0F&lk1s*AxZD$~F4jJN;+?_FOBxib{>`!gUUl_CHP4|UIJnJT5V9zF>}g_k8HTAY z-QiFXpRjYB5p#_SPY5|2r^&V>f`30+P?%v2Wpy4a$RjvLyhi-#aei!zcTD+ zLteQ|k|?$R$#p|IWC?wk&dK3Dfa&aTNs6dK$gtO0`KJm2#`JJ?wmJoybf8U18<9NC z#{vK3@QVb$sHEhCiF4m>8zj>@A)KIu@$}j z!H#Bw%bWcshhKhiXfWIL^OyX&I$9^f&S7PLH7FF?8@vW1lh?I1POLAY(yMSCQ$dt9 z>`ZGFE~ccV4Q%wW(z3%@nP#nvuRxGUwPVywXVmtCJ}CR?w%-U_Q@)!^!=To_r(B51 z?UDXJRSr0{nQOEEqXeSAmUg*r0_N~e3RFH=gv|6hn&j?D{n%#F+?OW(O^k7hlW5Po zTg;suPwaMRCfUo$LUKdX_?@H) z0@_N=iJ8u|hBJ$uU2p@mMI2>|Ae;w&G;1s+01|jlVSu#$e zQp3{i!{y`2n9j6@Bl!FNX%6t;0`cF+Sx`ioh4XGW3nK-)Z4w`uF#La| zzrF(#nphOTzXaiz`hD5$DgE7dMuh-^c;&;l;rgG_3T26*+8sgK-ZW-K1oVO4aJ-}q zUSsW$7P_<0q+{x1)A6ow&P49~n|@a>8TA$LPou3^ z_9`l${>=TvaGnA%ZxM_w^{gJp8~F>Ew$o19^W9UkK|3f~8r+Gt+1OzB-1?Dd`Q=d@a$HJ^hrbk-~o9n4n z8jgRXN6WFdgYqI=)rA7lj|5#ozFTlOsgYywjW<|iTDqpjcXXKu|BDf=0u&l&vi8*9 z`oxI~VSYDzo0`VkDaYvCOuY$Tw=PBX3fe0TSD!!t`Bt87leIqFoYwf#wTpKDEI9UH znrcWR{78R37gwLJQK^aM$>v-^Hd^&0EagUK~+h&)0h0 zm30fXudpo*>rQ@ZKojG6we_h!&1=4=`tqaBZiE>SW+GKL+ox3;Nu}pE=H7R7S%~WJ zA&ZM6uljnMti5{HAM)+T0VT%f?~Pkq2ZPDxY~I|rzs}G5MBYH9uJ|wgs}BUUy)CXE z`MlIR1}F*~9V}TxNL(ldC}uIe;OIahWjczt>KMfv5zS86mWFw%HqiEzvs8T_elVtl zN}uCuRB~n*)EK3aDvbJ&rF1$FfBCqPqXT9z8C|qi&Q1ocvU}09sQviVbNoM5EPRiI z;r}}eSC6~Dkh82d{ROaaEM=hDC!Awx>o)cgzEvT*>fXz$28!cLt;DpC!(x;R-`0Ms zw|GKhnn~FINHei{!c6)`3sgy{OhXI)nub2FDjOd4L$G6+2+ERGT&U&h+7;HM5Zp;L ztI@|Q@p#GN;;=>UMx~J>(+5X?FWv^4_lh*TUGI)0LnwYA?JNpG5)L0!zzd4(Cvf;t zm;Ty`{vxy2Ji2@oN=cuUGQ2B)<<~pXPS1y(Rd;VbJLT+jqYycx#o0O7 z=G*iyzv0pLwFeC8>(DBxp$-H)hHkE;4%gGyxBFw4EX?}vivORPw-rT+F~O%oDZJH^ zBC}exCi6dJj2AyoGhSsSC#t+Y%JO1>c`5jHs?!HW*qL9vP9h#3Pb9{`nmpcT(OGAN z6-zWQsb~(C-M30rOqf5AAfl^hMA}2ima4QgR+b*B&En+vTEM|UOkOg}!gZa_+C(s+ zR-64#L#3fAwMTobpZ5a5R$f-u3o8?jOxp;jUwiqr-(Q`bK1f5m=P2}mQbY%MEkX@? z43hzvWLj|35Ryn@%`rF;7GSj)7f>PSRBKslvvZ9kU0|(LPr%p-&B%JfyBgY!jMue0 z1*!iLbuuY@f7L#cd@hp{Tca(JxSnvNGsN?Qp8tvYu((8RjyDBbaOg@DF*6($^7owk z`nk*Z-?|9;O557_;>k5pk}98KfBFsfKi0pY8i#W7iWhLYUqMw6D@9$@#&KfZNa~QG zV=%=an`?>cq>(ebkS?2t;HkTHi4W#mYtvGQ{~wO)Zs!;xw7QRKkGV}zs^v5ws7WWPcXT92btxflB@MZNEjGV<6+cYu?8M>dg+@*3DB zj4g8PFZJ&0Z3;|;w@m@wm};iM%`g>X><}XCz$i@kX7-d_7~Z^~{mb*Wa!9yzT5!63 zLypTo!=Ej`^-Cp;4y7UpX1$#EAC5aaIl^8xC-dwgBz$ACDU!~@_}0jao|w+HV6W%z zf3!s@dSa{YUm~FThvDCQSEk zs;+3kUOW*V1n0T!;v%RhV+X}hN0$3qN5ydeviM@(6L$BOf(g=huO_=IEq*1Xqe{DF zu>8>=$H&CPubej-a0)JsO%`=kdSrZUi-8;TIZGsb{<3p6LWNl)zhU{gLx`on9zW^q zc{b0*(v0S&=eL+br;$V30p44lKM&@;nql9m`tahyf?)9`yz85G#i5MT`)z|;1~@9h zA3+?@W8J(rhfCkAz!_>~R4M^B4BLErv|{x6ki@eO(;@7&t(Q~G#DeoQbEXPr!8Vao z(0cheN}3F`SU`H83d?@KgG>%(2h6=9gjB4we8&9Q6o8F*i1ILB$sIN8{_C2dq zfyQdXjZhJN)pRDTuv(mSYU7Q%x@j&RgcR&&?$(9n#@NBlLWh;#DTP!>-zaof8fvq% zfkcd)@QozHKyJfkx~$gs^q^k2rJz4!s{Hy;WAwkcEA@^2wG3bFKfctgtL&5f(!gCC z)>>3Zbabg`NyvG872#wOF)`=65XO5`Yr|@Qe6nKlo+ZJ&^29j8{78mc`H1RS;(^YhY7pAI8nBWSvo z#zbuTYJr*734;i)GoWsEqm=?XH9Fm1kinG)k%={^R}cJ|kH6&ck$QTX^%wz@b<@Hx zLAPg0J?H{FWf;Z!`AIfQaAaI#LLeXT0$(p`l5OyGFpFX7=RdtX%gI)~Qv6WMk$qclmk6(W?-v1#^P z9EF3#IED5a4GIAlWALu-AM}VC{*g-aSL*B<18kh2)OxhS{a7&gFMtbvwESyM%COnn zbypsdY&R_!jBq`dtniUQO0#Al4FD_7j=${nvz4L>Zq`PItL_WmQCoKbl8A_ym-`73 zh=U#($z0d*!M}I``O53s8)y37-W2{FL3~i}cHn>NvhQ}#A;;nMku4nw8Kcbj)f@-~ z45+xp$Mw8zxbO3#LwZTsQ-QH#zDk@bP|a7P{@`%b((`z(_{EaUMdv=K_NoC7$s0Cz z#ytPGEQ|(-g(sbzqs(TSv!%VlYe5jfZN(3!trGo1ZrL%g!v_;9`$6jDlR>_*r7jTf zelShM`y$X;BzSOWyHSsp{i05;Z}QS*Cm3vEb-zD{ z{t7t+HDBru`tBf_U>8*uyAlYoam7eV3BmxQO4sp zoGf+tNq6O5Tmza(V&K~bgrN4M+*qbD>;-&PDGh%-v_FQBkn2?{=PcVcSPXb>qp(&) zvDYopSE9oHNslm{{1eiF4gg7%Vdqeq!G5**qi7U|OmeKu{?Ae{Gk}M*SJImuPMc?h z*4TQZvy4b-mK&9iNPa2!hlz4Rz$;U7Hh78l;9k8;0^*)j5stD!EiafhcRDYJ!}Z1c znDpIS(atO+&Q8q+5l%o$9S!*^_pxJa?@_%BIG%v&XspW>8~4`!pNT85L`I&{D`I3? zXV=wDl@5#D@lg4q(sl{h+s$vjSz7v+NIX`Jp%U@32uy|vM6T%E5l|bCDOddFc>%a# zJN*7J*rF|Px#-QDZm!%pKoUN3U|Dxjf$>)I`Qk6AcJ_XNcw$w#=`qR|>##jZGaXRL zv55G9!E@C8{Bp098EEib9`(Ti5~3*s>pan6g3qGsvr5D{V{1GJvuQqGCFAh9IztY` zTT+pzxC{fJQ7DKS!e$r`VaJ+mRpbCHRkqt8D*K93U6|&0mpXYr$ zkWcX4O@cxfU)YC^p5}#iI-xDYA(*F}9{D}y`y^uxHY;muh%b!1&3c++%FPR>r>E=N zZTVkP`|VKF!S_CHC?*})pqfEm@#pb%k%_z