auto commit

This commit is contained in:
CyC2018
2018-03-20 10:01:58 +08:00
parent 7cf8605f75
commit fa890fd48b
4 changed files with 166 additions and 124 deletions

View File

@ -1,29 +1,29 @@
<!-- GFM-TOC -->
* [使用线程](#使用线程)
* [1. 实现 Runnable 接口](#1-实现-runnable-接口)
* [2. 实现 Callable 接口](#2-实现-callable-接口)
* [3. 继承 Thread 类](#3-继承-thread-类)
* [4. 实现接口 vs 继承 Thread](#4-实现接口-vs-继承-thread)
* [Executor](#executor)
* [基础线程机制](#基础线程机制)
* [1. sleep()](#1-sleep)
* [2. yield()](#2-yield)
* [3. join()](#3-join)
* [4. deamon](#4-deamon)
* [线程之间的协作](#线程之间的协作)
* [1. 线程通信](#1-线程通信)
* [2. 线程同步](#2-线程同步)
* [2.1 synchronized](#21-synchronized)
* [2.2 Lock](#22-lock)
* [2.3 BlockingQueue](#23-blockingqueue)
* [结束线程](#结束线程)
* [1. 阻塞](#1-阻塞)
* [2. 中断](#2-中断)
* [线程状态转换](#线程状态转换)
* [volatile](#volatile)
* [1. 内存可见性](#1-内存可见性)
* [2. 禁止指令重排](#2-禁止指令重排)
* [内存模型](#内存模型)
* [一、使用线程](#使用线程)
* [实现 Runnable 接口](#实现-runnable-接口)
* [实现 Callable 接口](#实现-callable-接口)
* [继承 Thread 类](#继承-thread-类)
* [实现接口 VS 继承 Thread](#实现接口-vs-继承-thread)
* [二、Executor](#executor)
* [三、基础线程机制](#基础线程机制)
* [sleep()](#sleep)
* [yield()](#yield)
* [join()](#join)
* [deamon](#deamon)
* [四、线程之间的协作](#线程之间的协作)
* [线程通信](#线程通信)
* [线程同步](#线程同步)
* [1. synchronized](#1-synchronized)
* [2. Lock](#2-lock)
* [3. BlockingQueue](#3-blockingqueue)
* [五、结束线程](#结束线程)
* [阻塞](#阻塞)
* [中断](#中断)
* [六、线程状态转换](#线程状态转换)
* [七、volatile](#volatile)
* [保证内存可见性](#保证内存可见性)
* [禁止指令重排](#禁止指令重排)
* [八、内存模型](#内存模型)
* [1. 硬件的效率与一致性](#1-硬件的效率与一致性)
* [2. Java 内存模型](#2-java-内存模型)
* [3. 主内存与工作内存](#3-主内存与工作内存)
@ -33,7 +33,7 @@
* [5.2 可见性](#52-可见性)
* [5.3 有序性](#53-有序性)
* [6. 先行发生原则](#6-先行发生原则)
* [线程安全](#线程安全)
* [九、线程安全](#线程安全)
* [1. Java 语言中的线程安全](#1-java-语言中的线程安全)
* [1.1 不可变](#11-不可变)
* [1.2 绝对线程安全](#12-绝对线程安全)
@ -44,18 +44,18 @@
* [2.1 互斥同步](#21-互斥同步)
* [2.2 非阻塞同步](#22-非阻塞同步)
* [2.3 无同步方案](#23-无同步方案)
* [锁优化](#锁优化)
* [十、锁优化](#锁优化)
* [1. 自旋锁与自适应自旋](#1-自旋锁与自适应自旋)
* [2. 锁消除](#2-锁消除)
* [3. 锁粗化](#3-锁粗化)
* [4. 轻量级锁](#4-轻量级锁)
* [5. 偏向锁](#5-偏向锁)
* [多线程开发良好的实践](#多线程开发良好的实践)
* [十一、多线程开发良好的实践](#十一多线程开发良好的实践)
* [参考资料](#参考资料)
<!-- GFM-TOC -->
# 使用线程
# 一、使用线程
有三种使用线程的方法:
@ -65,7 +65,7 @@
实现 Runnable 和 Callable 接口的类只能当做一个可以在线程中运行的任务,不是真正意义上的线程,因此最后还需要通过 Thread 来调用。可以说任务是通过线程驱动从而执行的。
## 1. 实现 Runnable 接口
## 实现 Runnable 接口
需要实现 run() 方法。
@ -84,7 +84,7 @@ public class MyRunnable implements Runnable {
}
```
## 2. 实现 Callable 接口
## 实现 Callable 接口
与 Runnable 相比Callable 可以有返回值,返回值通过 FutureTask 进行封装。
@ -103,7 +103,7 @@ public class MyCallable implements Callable<Integer> {
}
```
## 3. 继承 Thread 类
## 继承 Thread 类
同样也是需要实现 run() 方法,并且最后也是调用 start() 方法来启动线程。
@ -119,14 +119,14 @@ public class MyThread extends Thread {
}
```
## 4. 实现接口 vs 继承 Thread
## 实现接口 VS 继承 Thread
实现接口会更好一些,因为:
1. Java 不支持多重继承,因此继承了 Thread 类就无法继承其它类,但是可以实现多个接口;
2. 类可能只要求可执行即可,继承整个 Thread 类开销会过大。
# Executor
# 二、Executor
Executor 管理多个异步任务的执行,而无需程序员显示地管理线程的生命周期。
@ -144,9 +144,9 @@ for(int i = 0; i < 5; i++) {
}
```
# 基础线程机制
# 三、基础线程机制
## 1. sleep()
## sleep()
**Thread.sleep(millisec)** 方法会休眠当前正在执行的线程millisec 单位为毫秒。也可以使用 TimeUnit.TILLISECONDS.sleep(millisec)。
@ -164,7 +164,7 @@ public void run() {
}
```
## 2. yield()
## yield()
对静态方法 **Thread.yield()** 的调用声明了当前线程已经完成了生命周期中最重要的部分,可以切换给其它线程来执行。
@ -175,13 +175,13 @@ public void run() {
}
```
## 3. join()
## join()
在线程中调用另一个线程的 **join()** 方法,会将当前线程挂起,直到目标线程结束。
可以加一个超时参数。
## 4. deamon
## deamon
后台线程( **deamon** )是程序运行时在后台提供服务的线程,并不属于程序中不可或缺的部分。
@ -191,14 +191,14 @@ main() 属于非后台线程。
使用 setDaemon() 方法将一个线程设置为后台线程。
# 线程之间的协作
# 四、线程之间的协作
- **线程通信** :保证线程以一定的顺序执行;
- **线程同步** :保证线程对临界资源的互斥访问。
线程通信往往是基于线程同步的基础上完成的,因此很多线程通信问题也是线程同步问题。
## 1. 线程通信
## 线程通信
**wait()、notify() 和 notifyAll()** 三者实现了线程之间的通信。
@ -229,11 +229,11 @@ public synchronized void before() {
1. wait() 是 Object 类的方法,而 sleep() 是 Thread 的静态方法;
2. wait() 会放弃锁,而 sleep() 不会。
## 2. 线程同步
## 线程同步
给定一个进程内的所有线程都共享同一存储空间这样有好处又有坏处。这些线程就可以共享数据非常有用。不过在两个线程同时修改某一资源时这也会造成一些问题。Java 提供了同步机制,以控制对共享资源的互斥访问。
### 2.1 synchronized
### 1. synchronized
**同步一个方法**
@ -255,7 +255,7 @@ public void func(String name) {
}
```
### 2.2 Lock
### 2. Lock
若要实现更细粒度的控制我们可以使用锁lock
@ -271,7 +271,7 @@ public int func(int value) {
}
```
### 2.3 BlockingQueue
### 3. BlockingQueue
java.util.concurrent.BlockingQueue 接口有以下阻塞队列的实现:
@ -365,9 +365,9 @@ Consumer3 is consuming product made by Consumer3...
Consumer4 is consuming product made by Consumer4...
```
# 结束线程
# 五、结束线程
## 1. 阻塞
## 阻塞
一个线程进入阻塞状态可能有以下原因:
@ -376,7 +376,7 @@ Consumer4 is consuming product made by Consumer4...
3. 等待某个 I/O 的完成;
4. 试图在某个对象上调用其同步控制方法,但是对象锁不可用,因为另一个线程已经获得了这个锁。
## 2. 中断
## 中断
使用中断机制即可终止阻塞的线程。
@ -402,7 +402,7 @@ interrupt() 方法会设置中断状态,可以通过 interrupted() 方法来
interrupted() 方法在检查完中断状态之后会清除中断状态,这样做是为了确保一次中断操作只会产生一次影响。
# 线程状态转换
# 六、线程状态转换
<div align="center"> <img src="../pics//38b894a7-525e-4204-80de-ecc1acc52c46.jpg"/> </div><br>
@ -427,11 +427,11 @@ interrupted() 方法在检查完中断状态之后会清除中断状态,这样
- LockSupport.parkNanos() 方法
- LockSupport.parkUntil() 方法
# volatile
# 七、volatile
保证了内存可见性和禁止指令重排,没法保证原子性。
## 1. 内存可见性
## 保证内存可见性
普通共享变量被修改之后,什么时候被写入主存是不确定的。
@ -439,7 +439,7 @@ volatile 关键字会保证每次修改共享变量之后该值会立即更新
synchronized 和 Lock 也能够保证内存可见性。它们能保证同一时刻只有一个线程获取锁然后执行同步代码,并且在释放锁之前会将对变量的修改刷新到主存当中。不过只有对共享变量的 set() 和 get() 方法都加上 synchronized 才能保证可见性,如果只有 set() 方法加了 synchronized那么 get() 方法并不能保证会从内存中读取最新的数据。
## 2. 禁止指令重排
## 禁止指令重排
在 Java 内存模型中,允许编译器和处理器对指令进行重排序,重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性。
@ -447,7 +447,7 @@ volatile 关键字通过添加内存屏障的方式来进制指令重排,即
可以通过 synchronized 和 Lock 来保证有序性,它们保证每个时刻只有一个线程执行同步代码,相当于是让线程顺序执行同步代码,自然就保证了有序性。
# 内存模型
# 八、内存模型
## 1. 硬件的效率与一致性
@ -581,7 +581,7 @@ int j = 2;
上面两个例子综合起来证明了一个结论:时间先后顺序与先行发生原则之间基本没有太大的关系,所以我们衡量并发安全问题的时候不要受到时间顺序的干扰,一切必须以先行发生原则为准。
# 线程安全
# 九、线程安全
《Java Concurrency In Practice》的作者 Brian Goetz 对“线程安全”有一个比较恰当的定义:“当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那这个对象是线程安全的”。
@ -830,7 +830,7 @@ incrementAndGet() 方法在一个无限循环中,不断尝试将一个比当
Java 语言中,如果一个变量要被多线程访问,可以使用 volatile 关键字声明它为“易变的”如果一个变量要被某个线程独享Java 中就没有类似 C++中 \_\_declspecthread这样的关键字不过还是可以通过 java.lang.ThreadLocal 类来实现线程本地存储的功能。每一个线程的 Thread 对象中都有一个 ThreadLocalMap 对象,这个对象存储了一组以 ThreadLocal.threadLocalHashCode 为键,以本地线程变量为值的 K-V 值对ThreadLocal 对象就是当前线程的 ThreadLocalMap 的访问入口,每一个 ThreadLocal 对象都包含了一个独一无二的 threadLocalHashCode 值,使用这个值就可以在线程 K-V 值对中找回对应的本地线程变量。
# 锁优化
# 十、锁优化
高效并发是从 JDK 1.5 到 JDK 1.6 的一个重要改进HotSpot 虚拟机开发团队在这个版本上花费了大量的精力去实现各种锁优化技术如适应性自旋Adaptive Spinning、锁消除Lock Elimination、锁粗化Lock Coarsening、轻量级锁Lightweight Locking和偏向锁Biased Locking等。这些技术都是为了在线程之间更高效地共享数据以及解决竞争问题从而提高程序的执行效率。
@ -917,7 +917,7 @@ public static String concatString(String s1, String s2, String s3) {
偏向锁可以提高带有同步但无竞争的程序性能。它同样是一个带有效益权衡Trade Off性质的优化也就是说它并不一定总是对程序运行有利如果程序中大多数的锁总是被多个不同的线程访问那偏向模式就是多余的。在具体问题具体分析的前提下有时候使用参数 -XX:-UseBiasedLocking 来禁止偏向锁优化反而可以提升性能。
# 多线程开发良好的实践
# 十一、多线程开发良好的实践
- 给线程命名。
- 最小化同步范围。