Update Java_并发编程.md

This commit is contained in:
heibaiying 2019-12-27 15:06:12 +08:00 committed by GitHub
parent 9a86a4c5f6
commit f66a0eadce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -156,7 +156,7 @@ Java 线程的生命周期分为以下五类状态:
### 1.4 线程终止 ### 1.4 线程终止
通常线程会随着代码的运行完成而终止,但如果你在线程中进行了循环操作,此时就需要考虑如何安全地停止线程?虽然 Thread 类提供了 `stop()` 方法,但其已经被标识为废弃,因为 `stop()` 只是暴力的停止线程, 但此时线程中的操作仍可能处于中间状态,此时暴力地停止就可能会产生非预期的结果。想要安全的停止线程,可以通过改变终止标志位的方式来实现: 通常线程会随着代码的运行完成而终止,但如果你在线程中进行了循环操作,此时就需要考虑如何安全地停止线程?虽然 Thread 类提供了 `stop()` 方法,但其已经被标识为废弃,因为 `stop()` 只是暴力的停止线程, 但此时线程中的操作仍可能处于中间状态,此时暴力地停止就可能会产生非预期的结果。想要安全的停止线程,可以通过改变终止标志位的方式来实现:
```java ```java
public class ThreadStop { public class ThreadStop {
@ -306,7 +306,7 @@ public class J1_ThreadUnsafe {
### 3.3 保证原子性 ### 3.3 保证原子性
通过 Java 虚拟机规范和非原子性协定, Java 语言可以保证对基本数据类型的访问具有原子性,如果想要保证更大范围内的原子性(如多行操作的原子性),此时可以使用字节码指令 monitorenter 和 monitorexit 来隐式执行 lock 和 unlock 操作从而将串行变成并行来保证原子性monitorenter 和 monitorexit 这两个字节码指令反映到 Java 代码中就是 synchronized 关键字。 通过 Java 虚拟机规范和非原子性协定, Java 语言可以保证对基本数据类型的访问具有原子性,如果想要保证更大范围内的原子性(如多行操作的原子性),此时可以使用字节码指令 monitorenter 和 monitorexit 来隐式执行 lock 和 unlock 操作从而将串行变成并行来保证原子性monitorenter 和 monitorexit 这两个字节码指令反映到 Java 代码中就是 synchronized 关键字。
@ -338,7 +338,7 @@ public class J1_ThreadUnsafe {
根据以上状态,当某个处理器对共享变量进行读写操作时,其具体的行为如下: 根据以上状态,当某个处理器对共享变量进行读写操作时,其具体的行为如下:
+ **读取共享变量**:处理器首先在高速缓存上进行查找,如果对应缓存条目的状态为 ME 或者 S此时则直接读取如果缓存条目为无效状态 I此时需要向总线发送 Read 消息,其他处理器或主内存则需要回复 Read Response 来提供相应的数据,处理器在获取到数据后,将其存储相应的缓存条目,并将状态更新为 S 。 + **读取共享变量**:处理器首先在高速缓存上进行查找,如果对应缓存条目的状态为 ME 或者 S此时则直接读取如果缓存条目为无效状态 I此时需要向总线发送 Read 消息,其他处理器或主内存则需要回复 Read Response 来提供相应的数据,处理器在获取到数据后,将其存储相应的缓存条目,并将状态更新为 S 。
+ **写入共享变量**:此时处理器首先需要判断是否拥有对该数据的所有权,如果对应缓存条目的状态为 E 或者 M代表此时均处于独占状态此时可以直接写入并将其状态变更为 M 。如果不为 E 或 M此时处理器需要往总线上发送 Invalidate 消息来通知其他处理器将对应的缓存条目失效,之后在收到其他处理器的 Invalidate Acknowledge 响应后再进行更改,并将其状态变更为 M。 + **写入共享变量**:此时处理器首先需要判断是否拥有对该数据的所有权,如果对应缓存条目的状态为 E 或者 M代表此时均处于独占状态此时可以直接写入并将其状态变更为 M 。如果不为 E 或 M此时处理器需要往总线上发送 Invalidate 消息来通知其他处理器将对应的缓存条目失效,之后在收到其他处理器的 Invalidate Acknowledge 响应后再进行更改,并将其状态变更为 M。
在只有高速缓存的情况下,通过缓存一致性协议能够保证一个线程对共享变量的更新对于其他线程是可见的。如果只是这样,多线程编程就不会存在可见性问题了,但实际上缓存一致性协议并不能保证最终的可见性,这是由于写缓冲器和无效化队列导致的。 在只有高速缓存的情况下,通过缓存一致性协议能够保证一个线程对共享变量的更新对于其他线程是可见的。如果只是这样,多线程编程就不会存在可见性问题了,但实际上缓存一致性协议并不能保证最终的可见性,这是由于写缓冲器和无效化队列导致的。
@ -524,7 +524,7 @@ public class J2_SynchronizedSafe {
} }
``` ```
通常我们把被修饰的方法体和代码块称为临界区,需要注意的是必须保证多线程锁住的是同一个临界区,否则依然是线程不安全的。如果将上面创建线程的方法修改为如下所示,此时 synchronized 锁住的是不同对象的 `inc()` 方法,所以仍然是线程不安全的: 通常我们把被修饰的方法体和代码块称为临界区,需要注意的是必须保证多线程锁住的是同一个临界区,否则依然是线程不安全的。如果将上面创建线程的方法修改为如下所示,此时 synchronized 锁住的是不同 IncreaseTask 对象的 `inc()` 方法,所以仍然是线程不安全的:
```java ```java
Thread thread1 = new Thread(new IncreaseTask()); Thread thread1 = new Thread(new IncreaseTask());
@ -1553,7 +1553,7 @@ new ThreadFactory() {
当线程池中可用线程的数量为 0并且等待队列已满的情况下线程池需要按照 RejectedExecutionHandler 指定的拒绝策略来决定如何处理后续提交任务JDK 中默认提供了以下四种拒绝策略: 当线程池中可用线程的数量为 0并且等待队列已满的情况下线程池需要按照 RejectedExecutionHandler 指定的拒绝策略来决定如何处理后续提交任务JDK 中默认提供了以下四种拒绝策略:
+ **ThreadPoolExecutor.AbortPolicy**:直接拒绝新提交的任务,并抛出异常; + **ThreadPoolExecutor.AbortPolicy**:直接拒绝新提交的任务,并抛出异常;
+ **ThreadPoolExecutor.DiscardPolicy**静默拒绝新提交的任务,并不抛出异常; + **ThreadPoolExecutor.DiscardPolicy**静默拒绝新提交的任务,并不抛出异常;
+ **ThreadPoolExecutor.DiscardOldestPolicy**:丢弃等待时间最长的任务,然后再尝试执行新提交的任务; + **ThreadPoolExecutor.DiscardOldestPolicy**:丢弃等待时间最长的任务,然后再尝试执行新提交的任务;
+ **ThreadPoolExecutor.CallerRunsPolicy**:直接在调用 execute 方法的线程中运行新提交的任务。 + **ThreadPoolExecutor.CallerRunsPolicy**:直接在调用 execute 方法的线程中运行新提交的任务。