Skip to content

Commit 29ffdf7

Browse files
committed
change image
1 parent 4e9cb7a commit 29ffdf7

16 files changed

+430
-99
lines changed

docs/HTTP.md

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
![img](../static/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3p1b3lpZ2VoYWl6ZWk=,size_16,color_FFFFFF,t_70.png)
1414

15-
在发起连接之前,服务器会将自己的公钥发给CA证书机构,CA证书机构会用自己的私钥对服务器的公钥加密,生成CA证书给服务器,服务器存储后供之后建立安全连接使用
15+
在发起连接之前,服务器会向证书机构申请SSL证书,流程是服务器将自己的公钥发给CA证书机构,CA证书机构会用自己的私钥对服务器的公钥加密,生成CA证书给服务器,服务器将SSL证书存储后供之后建立安全连接使用
1616

1717
1.客户端发起请求,TCP三次握手,跟服务器建立连接。
1818
如上图所示,在第 ② 步时服务器发送了一个SSL证书给客户端,SSL 证书中包含的具体内容有:
@@ -46,8 +46,6 @@
4646
假设没有CA,那么如果服务器返回的包含公钥的包被hack截取,然后hack也生成一对公私钥,他将自己的公钥发给客户端。hack得到客户端数据后,解密,然后再通过服务器的公钥加密发给服务器,这样数据就被hack获取。
4747
有了CA后,客户端根据内置的CA根证书,很容易识别出hack的公钥不合法,或者说hack的证书不合法。
4848

49-
50-
5149
### HTTP的缓存策略是怎么样的?
5250

5351
HTTP 缓存主要分为强缓存和对比缓存两种,从优先级上看,强缓存大于对比缓存。
@@ -155,7 +153,7 @@ TCP主要提供了**检验和**、**序列号/确认应答**、**超时重传**
155153

156154
### 流量控制是怎么实现的?
157155

158-
因为发送端在发送的数据包的序号必须小于最大的滑动窗口值,所以当发送的数据包过多,导致接收端的缓冲区写满时,接收端会通知给客户端将滑动窗口设置为更小的值,减少发送的量,达到一个流量控制的效果。
156+
因为滑动窗口设置得太大或太小都不易于数据传输,所以是根据接收端的反馈,发送端可以对滑动窗口大小进行动态调整的。发送端在发送的数据包的序号必须小于最大的滑动窗口值,所以当发送的数据包过多,导致接收端的缓冲区写满时,接收端会通知给客户端将滑动窗口设置为更小的值,减少发送的量,达到一个流量控制的效果。
159157

160158
### TCP拥塞控制怎么实现?
161159

@@ -187,12 +185,22 @@ TCP连接刚建立,一点一点地提速,试探一下网络的承受能力
187185

188186
### close_wait 太多怎么处理?为什么会出现这种情况?
189187

190-
close_wait 主要在TCP四次挥手时,被动关闭方给主动关闭方返回ACK应答后,由于自身还需要给主动方传输数据,所以会进入到close_wait状态,直到不需要给主动方发数据了,才会去给主动关闭方发送FIN包,同时进入LAST_ACK状态。
191-
192-
一般如果我们使用JDBC去操作数据库,如果在完成操作后不关闭与数据库的连接,就会造成数据库连接不会关闭,进入close_wait状态。
188+
close_wait 主要在TCP四次挥手时,服务端给客户端返回ACK应答后,由于自身还需要给客户端传输数据,所以会进入到close_wait状态,直到不需要给客户端发数据了,才会去给客户端发送FIN包,同时进入LAST_ACK状态。(被动关闭的一方没有及时发出 FIN 包就会导致自己一直处于close_wait状态。)
193189

194190
tcp_keepalive_time默认是2个小时,也就是TCP空闲连接可以存活2个小时,在close_wait状态下,可以把这个时间调小,减少处于close_wait连接的数量
195191

192+
### time_wait太多是怎么造成的?
193+
首先time_wait状态存在的意义是可以有效地终止TCP连接,因为主动关闭方发生ACK给被动关闭方后,需要等待2MSL的时间(MSL指的是报文最大有效存活时间,在linux下是60s),在这个时间内,如果没有收到被动关闭方重发的FIN包,就说明连接关闭完成了。
194+
在高并发短连接的业务场景下,由于短连接的传输数据+业务处理的时间很短,所以服务器处理完请求就会立即主动关闭连接,并且进入TIME_WAITING状态,而端口处于有个0~65535的范围中,除去系统占用的,总的数量有限。所以持续的到达一定量的高并发短连接,会使服务器因端口资源不足而拒绝为一部分请求服务。
195+
可以通过修改TCP的默认配置来改善这个问题。
196+
```
197+
net.ipv4.tcp_syncookies = 1 表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭;
198+
net.ipv4.tcp_tw_reuse = 1 表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭;
199+
net.ipv4.tcp_tw_recycle = 1 表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。
200+
net.ipv4.tcp_fin_timeout 修改系默认的 TIMEOUT 时间
201+
```
202+
https://www.cnblogs.com/dadonggg/p/8778318.html
203+
https://segmentfault.com/a/1190000019292140
196204
### HTTP/2 有哪些新特性?
197205
#### 1.二进制传输
198206
HTTP/2传输数据量的大幅减少,主要有两个原因:以二进制方式传输和Header 压缩。我们先来介绍二进制传输,HTTP/2 采用二进制格式传输数据,而非HTTP/1.x 里纯文本形式的报文 ,二进制协议解析起来更高效。HTTP/2 将请求和响应数据分割为更小的帧,并且它们采用二进制编码。

docs/JavaJVM.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -513,7 +513,7 @@ Java中的类在编译后会生成class文件,class文件除了包含变量,
513513

514514
**吞吐量高**
515515

516-
移动时内存操作会比较复杂,需要移动存活对象并且更新所有对象的引用,会是一种比较重的操作,但是如果不移动的话,会有内存碎片,内存分配时效率会变低,所以由于内存分配的频率会比垃圾回收的频率高很多,所以从吞吐量方面看,标记-整理法高于标记-清除法,所以强调高吞吐量的Parallel Scavenge收集器是采用标记整理法
516+
移动时内存操作会比较复杂,需要移动存活对象并且更新所有对象的引用,会是一种比较重的操作,但是如果不移动的话,会有内存碎片,内存分配时效率会变低,所以由于内存分配的频率会比垃圾回收的频率高很多,所以从吞吐量方面看,标记-整理法高于标记-清除法。
517517

518518
**延迟高**
519519

@@ -878,11 +878,10 @@ JDK8默认情况下服务端模式下JVM垃圾回收参数是-XX:+UseParallelGC
878878

879879
![img](../static/519126-20180623154635076-953076776.png)
880880

881-
882-
883881
### 容器的内存和 jvm 的内存有什么关系?参数怎么配置?
884882

885883
一般在使用容器部署Java应用时,一般为了充分利用物理机的资源,会在物理机上部署多个容器应用,然后对每个容器设置最大内存的限制,但是JVM的最大堆默认值一般取得的物理机最大内存的25%,一旦应用内存超出容器的最大内存限制,容器就会把应用进程kill掉,然后重启。为了解决这个问题,有3种解决方案:
884+
886885
1.在应用的JVM参数中添加-Xmx 最大堆内存的大小,可以设置为容器最大内存限制的75%。一旦你在修改了容器的最大内存限制,每个应用的JVM参数-Xmx 也许需要同步进行修改。
887886

888887
2.就是添加这几个参数可以让Java应用感知容器的内存限制,从而在设置最大堆内存大小时,根据容器的内存限制进行设置。
@@ -904,7 +903,7 @@ JDK8默认情况下服务端模式下JVM垃圾回收参数是-XX:+UseParallelGC
904903

905904
Max memory = [-Xmx] + [-XX:MaxPermSize] + number_of_threads * [-Xss]
906905

907-
-Xss128k:设置每个线程的堆栈大小.JDK5.0以后每个线程的栈大小为1M
906+
-Xss128k: 设置每个线程的堆栈大小.JDK5.0以后默认每个线程的栈大小为1M
908907

909908
-Xms 堆内存的初始大小,默认为物理内存的1/64
910909
-Xmx 堆内存的最大大小,默认为物理内存的1/4
@@ -1006,6 +1005,7 @@ KiB Swap: 16777212 total, 16776604 free, 608 used. 13312484 avail Mem
10061005
```
10071006
2.使用`top -Hp 进程id`获得该进程下各个线程的CPU占用情况,找到占用率最高的线程的pid2,
10081007
使用`printf "%x\n" pid2`命令将pid2转换为16进制的数number。
1008+
10091009
```
10101010
top - 15:11:01 up 523 days, 3:48, 1 user, load average: 0.00, 0.01, 0.05
10111011
Threads: 69 total, 0 running, 69 sleeping, 0 stopped, 0 zombie
@@ -1162,7 +1162,7 @@ public static void main(String[] args) {
11621162

11631163
##### jmap -histo
11641164

1165-
**jmap -histo**打印出当前堆中的对象统计信息,包括类名,每个类的实例数量,总占用内存大小。
1165+
**jmap -histo 进程id** 打印出当前堆中的对象统计信息,包括类名,每个类的实例数量,总占用内存大小。
11661166

11671167
```java
11681168
instances列:表示当前类有多少个实例。
@@ -1264,4 +1264,5 @@ public class Test018 {
12641264

12651265
![img](../static/640-20200726210041919.jpeg)
12661266

1267-
![img](../static/640-20200726210058837.jpeg)
1267+
![img](../static/640-20200726210058837.jpeg)
1268+

docs/JavaMultiThread.md

Lines changed: 157 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -356,7 +356,9 @@ Future也是一个接口,Future就像是一个管理的容器一样,进一
356356

357357
```java
358358
public interface Future<V> {
359-
359+
//mayInterruptIfRunning代表是否强制中断
360+
//为true,如果任务已经执行,那么会调用Thread.interrupt()方法设置中断标识
361+
//为false,如果任务已经执行,就只会将任务状态标记为取消,而不会去设置中断标识
360362
boolean cancel(boolean mayInterruptIfRunning);
361363

362364
boolean isCancelled();
@@ -642,7 +644,7 @@ public enum State {
642644
643645
#### BLOCKED 阻塞态
644646

645-
阻塞状态。处于BLOCKED状态的线程正等待锁的释放以进入同步区
647+
阻塞状态。线程没有申请到synchronize同步锁,就会处于阻塞状态,等待锁的释放以进入同步区
646648

647649
#### WAITING 等待态
648650

@@ -825,11 +827,158 @@ thread.interrupted()
825827

826828
### 线程执行的任务可以终止吗?
827829

830+
##### 1.设置中断
831+
828832
FutureTask提供了cancel(boolean mayInterruptIfRunning)方法来取消任务,并且
829833

830834
如果入参为false,如果任务已经在执行,那么任务就不会被取消。
831835

832-
如果入参为true,如果任务已经在执行,那么会调用Thread的interrupt()方法来设置线程的中断标识,如果线程处于阻塞状态,会抛出异常,如果正常状态只是设置标志位,修改interrupted变量的值。所以如果要取消任务只能在任务内部中调用thread.isInterrupted()方法获取当前线程的中断状态,自行取消。
836+
如果入参为true,如果任务已经在执行,那么会调用Thread的interrupt()方法来设置线程的中断标识,如果线程处于阻塞状态,会抛出InterruptedException异常,如果正常状态只是设置标志位,修改interrupted变量的值。所以如果要取消任务只能在任务内部中调用thread.isInterrupted()方法获取当前线程的中断状态,自行取消。
837+
838+
##### 2.线程的stop方法
839+
840+
线程的stop()方法可以让线程停止执行,但是不会释放当前线程持有的锁。但是这个方法已经被标记为过期,不推荐使用了。
841+
842+
### 让线程顺序执行有哪些方法?
843+
844+
##### 1.主线程Join
845+
846+
就是调用threadA.start()方法让线程A先执行,然后主线程调用threadA.join()方法,然后主线程进入TIME_WAITING状态,直到threadA执行结束后,主线程才能继续往下执行,执行线程B的任务。(join方法的底层实现其实是调用了threadA的wait()方法,当线程A执行完毕后,会自动调用notifyAll()方法唤醒所有线程。)
847+
848+
示例代码如下:
849+
850+
```java
851+
Thread threadA = new Thread(new Runnable() {
852+
@Override
853+
public void run() {
854+
//执行threadA的任务
855+
}
856+
});
857+
Thread threadB= new Thread(new Runnable() {
858+
@Override
859+
public void run() {
860+
//执行threadB的任务
861+
}
862+
});
863+
//执行线程A任务
864+
threadA.start();
865+
//主线程进行等待
866+
threadA.join();
867+
//执行线程B的任务
868+
threadB.start();
869+
```
870+
871+
##### 子线程Join
872+
873+
就是让线程B的任务在执行时,调用threadA.join()方法,这样就只有等线程A的任务执行完成后,才会执行线程B。
874+
875+
```java
876+
Thread threadA = new Thread(new Runnable() {
877+
@Override
878+
public void run() {
879+
//执行threadA的任务
880+
}
881+
});
882+
Thread threadB= new Thread(new Runnable() {
883+
@Override
884+
public void run() {
885+
//子线程进行等待,知道threadA任务执行完毕
886+
threadA.join();
887+
//执行threadB的任务
888+
}
889+
});
890+
//执行线程A任务
891+
threadA.start();
892+
//执行线程B的任务
893+
threadB.start();
894+
```
895+
##### 单线程池法
896+
897+
就是使用Executors.newSingleThreadExecutor()这个线程池,这个线程池的特点就是只有一个执行线程,可以保证任务按顺序执行。
898+
899+
```java
900+
ExecutorService pool = Executors.newSingleThreadExecutor();
901+
//提交任务A
902+
executorService.submit(taskA);
903+
//提交任务B
904+
executorService.submit(taskB);
905+
```
906+
907+
##### 等待通知法(wait和notify)
908+
909+
就是在线程B中调用Object.waiting()方法进行等待,线程A执行完毕后调用Object.notify()方法进行唤醒。(这种方法有两个缺点,一个是Object.waiting()和notify()方法必须在同步代码块中调用,第二个是如果线程A执行过快,先调用了object.notify()方法,就会导致线程B后面一直得不到唤醒。)
910+
911+
```java
912+
final Object object = new Object();
913+
Thread threadA = new Thread(new Runnable() {
914+
@Override
915+
public void run() {
916+
//执行threadA的任务
917+
synchronized(object) {
918+
object.notify();
919+
}
920+
}
921+
});
922+
Thread threadB= new Thread(new Runnable() {
923+
@Override
924+
public void run() {
925+
synchronized(object) {
926+
//子线程进行等待,知道threadA任务执行完毕
927+
object.wait();
928+
//执行threadB的任务
929+
}
930+
}
931+
});
932+
```
933+
934+
##### 等待通知法(await和singal)
935+
936+
具体实现就是Reentrantlock可以创建出一个Condition实例queue,可以认为是一个等待队列,线程B调用queue.await()就会进行等待,直到线程A执行完毕调用queue.signal()来唤醒线程B。
937+
938+
```java
939+
final ReentrantLock lock = new ReentrantLock();
940+
final Condition queue1 = lock.newCondition();
941+
final Object object = new Object();
942+
final Thread threadA = new Thread(new Runnable() {
943+
@Override
944+
public void run() {
945+
//执行threadA的任务
946+
lock.lock();
947+
try {
948+
//唤醒线程B的任务
949+
queue1.signal();
950+
} catch (InterruptedException e) {
951+
e.printStackTrace();
952+
}
953+
System.out.println("执行了任务A2");
954+
lock.unlock();
955+
}
956+
});
957+
final Thread threadB= new Thread(new Runnable() {
958+
@Override
959+
public void run() {
960+
lock.lock();
961+
//子线程进行等待,知道threadA任务执行完毕
962+
try {
963+
queue1.await();
964+
System.out.println("执行了任务B2");
965+
966+
} catch (InterruptedException e) {
967+
e.printStackTrace();
968+
}
969+
//执行threadB的任务
970+
lock.unlock();
971+
}
972+
});
973+
threadA.start();
974+
threadB.start();
975+
```
976+
977+
978+
979+
参考链接:
980+
981+
http://cnblogs.com/wenjunwei/p/10573289.html
833982

834983
### 线程间怎么通信?
835984

@@ -986,8 +1135,6 @@ Integer consumer() {
9861135
return value;
9871136
}
9881137
}
989-
990-
9911138
```
9921139

9931140
完整代码如下:
@@ -1436,10 +1583,11 @@ public ScheduledThreadPoolExecutor(int corePoolSize,
14361583
最佳线程数=CPU的数量+1
14371584
```
14381585
#### 方法二 IO密集型任务
1439-
这种任务在执行时,需要进行一些IO操作,所以为了充分利用CPU,应该在线程进行IO操作时,就让出时间片,CPU进行上下文切换,执行其他线程的任务,保证CPU利用率的100%
1586+
这种任务在执行时,需要进行一些IO操作,所以为了充分利用CPU,应该在线程进行IO操作时,就让出时间片,CPU进行上下文切换,执行其他线程的任务,保证CPU利用率尽可能达到100%
14401587

1441-
如果任务有50%的时间需要CPU执行状态,其他时间进行IO操作,则程序所需线程数为CPU数量的1除以0.5,也就是2倍。如果任务有20%的时时间需要CPU执行,其他时间需要进行IO操作,最佳线程数也就是1除以0.2,也就是CPU数的5
1588+
如果任务有50%的时间需要CPU执行状态,其他时间进行IO操作,则程序所需线程数为CPU数量的1除以0.5,也就是2倍。如果任务有20%的时时间需要CPU执行,其他时间需要进行IO操作,最佳线程数也就是1除以0.2,也就是CPU数的5
14421589
所以公式为
1590+
14431591
```java
14441592
最佳线程数 = CPU数量/(每个任务中需要CPU执行的时间的比例)
14451593
= CPU数量/(CPU运行时间/任务执行总时间)=CPU数量/(CPU运行时间/(CPU运行时间+IO操作时间))
@@ -1455,6 +1603,7 @@ public ScheduledThreadPoolExecutor(int corePoolSize,
14551603
一种是追求响应时间的任务,例如使用线程池对发起多个网络请求,然后对结果进行计算。 这种任务的最大线程数需要设置大一点,然后队列使用同步队列,队列中不缓存任务,任务来了就会被执行。判断线程池资源不够用时,一般是发现活跃线程数/最大线程数>阀值(默认是0.8)时,或者是线程池抛出的RejectedExecut异常次数达到阀值,就会进行告警。然后程序员收到告警后,动态发送修改核心线程数,最大线程数,队列相关的指令,服务器进行动态修改。
14561604

14571605
##### 追求高吞吐量的任务
1606+
14581607
假设说需要定期自动化生成一些报表,不需要考虑响应时间,只是希望如何使用有限的资源,尽可能在单位时间内处理更多的任务,也就是吞吐量优先的问题。
14591608
这种就是使用有界队列,对任务进行缓存,然后线程进行并发执行。判断线程池资源不够用时,一般是发现等待队列中的任务数量/等待队列的长度>阀值(默认是0.8)时,或者是线程池抛出的RejectedExecut异常次数达到阀值,就会进行告警。然后程序员收到告警后,动态发送修改核心线程数,最大线程数,队列相关的指令,服务器进行动态修改。
14601609

@@ -1544,7 +1693,7 @@ ThreadLocal变量所在的类的实例(代码中A的实例)->ThreadLocal
15441693

15451694
可以看到ThreadLocal变量不仅被所在的类A的实例所引用,还被执行的线程所引用,
15461695

1547-
1.如果使用强引用,也就是线程对ThreadLocal变量是强引用,那么类A的实例即便已经不被其他变量所引用了,不会被访问到了,需要被回收了,类A被回收了,但是ThreadLocal变量由于有被执行线程所引用,只要线程还在,ThreadLocal变量也不能被被JVM回收,所以会造成内存泄露
1696+
1.如果使用强引用,也就是线程对ThreadLocal变量是强引用,那么即便实例A被回收了,只要线程还没有被回收,线程的ThreadLocalMap还会引用这个key(也就是这个ThreadLocal遍历),导致这个key 没有被回收,造成内存泄露
15481697

15491698
2.如果使用弱引用,不会影响key的回收,也就是不会影响引用了ThreadLocal的实例对象的回收。
15501699

0 commit comments

Comments
 (0)