Java 并发.md 61.4 KB
Newer Older
C
CyC2018 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
<!-- GFM-TOC -->
* [一、线程状态转换](#一线程状态转换)
    * [新建(New)](#新建new)
    * [可运行(Runnable)](#可运行runnable)
    * [阻塞(Blocking)](#阻塞blocking)
    * [无限期等待(Waiting)](#无限期等待waiting)
    * [限期等待(Timed Waiting)](#限期等待timed-waiting)
    * [死亡(Terminated)](#死亡terminated)
* [二、使用线程](#二使用线程)
    * [实现 Runnable 接口](#实现-runnable-接口)
    * [实现 Callable 接口](#实现-callable-接口)
    * [继承 Thread 类](#继承-thread-类)
    * [实现接口 VS 继承 Thread](#实现接口-vs-继承-thread)
* [三、基础线程机制](#三基础线程机制)
    * [Executor](#executor)
    * [Daemon](#daemon)
    * [sleep()](#sleep)
    * [yield()](#yield)
* [四、中断](#四中断)
    * [InterruptedException](#interruptedexception)
    * [interrupted()](#interrupted)
    * [Executor 的中断操作](#executor-的中断操作)
* [五、互斥同步](#五互斥同步)
    * [synchronized](#synchronized)
    * [ReentrantLock](#reentrantlock)
C
CyC2018 已提交
26 27
    * [比较](#比较)
    * [使用选择](#使用选择)
C
CyC2018 已提交
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
* [六、线程之间的协作](#六线程之间的协作)
    * [join()](#join)
    * [wait() notify() notifyAll()](#wait-notify-notifyall)
    * [await() signal() signalAll()](#await-signal-signalall)
* [七、J.U.C - AQS](#七juc---aqs)
    * [CountdownLatch](#countdownlatch)
    * [CyclicBarrier](#cyclicbarrier)
    * [Semaphore](#semaphore)
* [八、J.U.C - 其它组件](#八juc---其它组件)
    * [FutureTask](#futuretask)
    * [BlockingQueue](#blockingqueue)
    * [ForkJoin](#forkjoin)
* [九、线程不安全示例](#九线程不安全示例)
* [十、Java 内存模型](#十java-内存模型)
    * [主内存与工作内存](#主内存与工作内存)
    * [内存间交互操作](#内存间交互操作)
    * [内存模型三大特性](#内存模型三大特性)
    * [先行发生原则](#先行发生原则)
* [十一、线程安全](#十一线程安全)
    * [线程安全定义](#线程安全定义)
    * [线程安全分类](#线程安全分类)
    * [线程安全的实现方法](#线程安全的实现方法)
* [十二、锁优化](#十二锁优化)
    * [自旋锁](#自旋锁)
    * [锁消除](#锁消除)
    * [锁粗化](#锁粗化)
    * [轻量级锁](#轻量级锁)
    * [偏向锁](#偏向锁)
* [十三、多线程开发良好的实践](#十三多线程开发良好的实践)
* [参考资料](#参考资料)
<!-- GFM-TOC -->


# 一、线程状态转换

<div align="center"> <img src="../pics//ace830df-9919-48ca-91b5-60b193f593d2.png" width=""/> </div><br>

## 新建(New)
C
CyC2018 已提交
66 67 68

创建后尚未启动。

C
CyC2018 已提交
69
## 可运行(Runnable)
C
CyC2018 已提交
70

C
CyC2018 已提交
71
可能正在运行,也可能正在等待 CPU 时间片。
C
CyC2018 已提交
72

C
CyC2018 已提交
73
包含了操作系统线程状态中的 Running 和 Ready。
C
CyC2018 已提交
74

C
CyC2018 已提交
75
## 阻塞(Blocking)
C
CyC2018 已提交
76 77 78

等待获取一个排它锁,如果其线程释放了锁就会结束此状态。

C
CyC2018 已提交
79
## 无限期等待(Waiting)
C
CyC2018 已提交
80

C
CyC2018 已提交
81
等待其它线程显式地唤醒,否则不会被分配 CPU 时间片。
C
CyC2018 已提交
82

C
CyC2018 已提交
83 84 85 86 87
| 进入方法 | 退出方法 |
| --- | --- |
| 没有设置 Timeout 参数的 Object.wait() 方法 | Object.notify() / Object.notifyAll() |
| 没有设置 Timeout 参数的 Thread.join() 方法 | 被调用的线程执行完毕 |
| LockSupport.park() 方法 | - |
C
CyC2018 已提交
88

C
CyC2018 已提交
89
## 限期等待(Timed Waiting)
C
CyC2018 已提交
90

C
CyC2018 已提交
91
无需等待其它线程显式地唤醒,在一定时间之后会被系统自动唤醒。
C
CyC2018 已提交
92

C
CyC2018 已提交
93
调用 Thread.sleep() 方法使线程进入限期等待状态时,常常用“使一个线程睡眠”进行描述。
C
CyC2018 已提交
94

C
CyC2018 已提交
95
调用 Object.wait() 方法使线程进入限期等待或者无限期等待时,常常用“挂起一个线程”进行描述。
C
CyC2018 已提交
96

C
CyC2018 已提交
97 98
睡眠和挂起是用来描述行为,而阻塞和等待用来描述状态。

C
CyC2018 已提交
99
阻塞和等待的区别在于,阻塞是被动的,它是在等待获取一个排它锁。而等待是主动的,通过调用 Thread.sleep() 和 Object.wait() 等方法进入。
C
CyC2018 已提交
100

C
CyC2018 已提交
101 102 103 104 105 106 107
| 进入方法 | 退出方法 |
| --- | --- |
| Thread.sleep() 方法 | 时间结束 |
| 设置了 Timeout 参数的 Object.wait() 方法 | 时间结束 / Object.notify() / Object.notifyAll()  |
| 设置了 Timeout 参数的 Thread.join() 方法 | 时间结束 / 被调用的线程执行完毕 |
| LockSupport.parkNanos() 方法 | - |
| LockSupport.parkUntil() 方法 | - |
C
CyC2018 已提交
108

C
CyC2018 已提交
109
## 死亡(Terminated)
C
CyC2018 已提交
110 111 112

可以是线程结束任务之后自己结束,或者产生了异常而结束。

C
CyC2018 已提交
113
# 二、使用线程
C
CyC2018 已提交
114 115 116

有三种使用线程的方法:

C
CyC2018 已提交
117 118 119
- 实现 Runnable 接口;
- 实现 Callable 接口;
- 继承 Thread 类。
C
CyC2018 已提交
120

C
CyC2018 已提交
121
实现 Runnable 和 Callable 接口的类只能当做一个可以在线程中运行的任务,不是真正意义上的线程,因此最后还需要通过 Thread 来调用。可以说任务是通过线程驱动从而执行的。
C
CyC2018 已提交
122

C
CyC2018 已提交
123
## 实现 Runnable 接口
C
CyC2018 已提交
124

C
CyC2018 已提交
125
需要实现 run() 方法。
C
CyC2018 已提交
126

C
CyC2018 已提交
127
通过 Thread 调用 start() 方法来启动线程。
C
CyC2018 已提交
128 129

```java
C
CyC2018 已提交
130 131 132 133
public class MyRunnable implements Runnable {
    public void run() {
        // ...
    }
C
CyC2018 已提交
134 135 136 137
}
```

```java
C
CyC2018 已提交
138 139 140 141
public static void main(String[] args) {
    MyRunnable instance = new MyRunnable();
    Thread thread = new Thread(instance);
    thread.start();
C
CyC2018 已提交
142 143 144
}
```

C
CyC2018 已提交
145
## 实现 Callable 接口
C
CyC2018 已提交
146

C
CyC2018 已提交
147
与 Runnable 相比,Callable 可以有返回值,返回值通过 FutureTask 进行封装。
C
CyC2018 已提交
148 149

```java
C
CyC2018 已提交
150 151 152 153
public class MyCallable implements Callable<Integer> {
    public Integer call() {
        return 123;
    }
C
CyC2018 已提交
154 155 156 157
}
```

```java
C
CyC2018 已提交
158 159 160 161 162 163
public static void main(String[] args) throws ExecutionException, InterruptedException {
    MyCallable mc = new MyCallable();
    FutureTask<Integer> ft = new FutureTask<>(mc);
    Thread thread = new Thread(ft);
    thread.start();
    System.out.println(ft.get());
C
CyC2018 已提交
164 165 166
}
```

C
CyC2018 已提交
167
## 继承 Thread 类
C
CyC2018 已提交
168

C
CyC2018 已提交
169
同样也是需要实现 run() 方法,因为 Thread 类也实现了 Runable 接口。
C
CyC2018 已提交
170 171

```java
C
CyC2018 已提交
172 173 174 175
public class MyThread extends Thread {
    public void run() {
        // ...
    }
C
CyC2018 已提交
176 177 178 179
}
```

```java
C
CyC2018 已提交
180 181 182
public static void main(String[] args) {
    MyThread mt = new MyThread();
    mt.start();
C
CyC2018 已提交
183 184 185
}
```

C
CyC2018 已提交
186
## 实现接口 VS 继承 Thread
C
CyC2018 已提交
187 188 189

实现接口会更好一些,因为:

C
CyC2018 已提交
190 191
- Java 不支持多重继承,因此继承了 Thread 类就无法继承其它类,但是可以实现多个接口;
- 类可能只要求可执行就行,继承整个 Thread 类开销过大。
C
CyC2018 已提交
192

C
CyC2018 已提交
193
# 三、基础线程机制
C
CyC2018 已提交
194

C
CyC2018 已提交
195
## Executor
C
CyC2018 已提交
196

C
CyC2018 已提交
197
Executor 管理多个异步任务的执行,而无需程序员显式地管理线程的生命周期。这里的异步是指多个任务的执行互不干扰,不需要进行同步操作。
C
CyC2018 已提交
198

C
CyC2018 已提交
199
主要有三种 Executor:
C
CyC2018 已提交
200

C
CyC2018 已提交
201 202 203
- CachedThreadPool:一个任务创建一个线程;
- FixedThreadPool:所有任务只能使用固定大小的线程;
- SingleThreadExecutor:相当于大小为 1 的 FixedThreadPool。
C
CyC2018 已提交
204 205

```java
C
CyC2018 已提交
206 207 208 209 210 211
public static void main(String[] args) {
    ExecutorService executorService = Executors.newCachedThreadPool();
    for (int i = 0; i < 5; i++) {
        executorService.execute(new MyRunnable());
    }
    executorService.shutdown();
C
CyC2018 已提交
212 213 214
}
```

C
CyC2018 已提交
215
## Daemon
C
CyC2018 已提交
216 217 218 219 220

守护线程是程序运行时在后台提供服务的线程,不属于程序中不可或缺的部分。

当所有非守护线程结束时,程序也就终止,同时会杀死所有守护线程。

C
CyC2018 已提交
221
main() 属于非守护线程。
C
CyC2018 已提交
222

C
CyC2018 已提交
223
使用 setDaemon() 方法将一个线程设置为守护线程。
C
CyC2018 已提交
224 225

```java
C
CyC2018 已提交
226 227 228
public static void main(String[] args) {
    Thread thread = new Thread(new MyRunnable());
    thread.setDaemon(true);
C
CyC2018 已提交
229 230 231
}
```

C
CyC2018 已提交
232
## sleep()
C
CyC2018 已提交
233

C
CyC2018 已提交
234
Thread.sleep(millisec) 方法会休眠当前正在执行的线程,millisec 单位为毫秒。
C
CyC2018 已提交
235

C
CyC2018 已提交
236
sleep() 可能会抛出 InterruptedException,因为异常不能跨线程传播回 main() 中,因此必须在本地进行处理。线程中抛出的其它异常也同样需要在本地进行处理。
C
CyC2018 已提交
237 238

```java
C
CyC2018 已提交
239 240 241 242 243 244
public void run() {
    try {
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
C
CyC2018 已提交
245 246 247
}
```

C
CyC2018 已提交
248
## yield()
C
CyC2018 已提交
249

C
CyC2018 已提交
250
对静态方法 Thread.yield() 的调用声明了当前线程已经完成了生命周期中最重要的部分,可以切换给其它线程来执行。该方法只是对线程调度器的一个建议,而且也只是建议具有相同优先级的其它线程可以运行。
C
CyC2018 已提交
251 252

```java
C
CyC2018 已提交
253 254
public void run() {
    Thread.yield();
C
CyC2018 已提交
255 256 257
}
```

C
CyC2018 已提交
258
# 四、中断
C
CyC2018 已提交
259 260 261

一个线程执行完毕之后会自动结束,如果在运行过程中发生异常也会提前结束。

C
CyC2018 已提交
262
## InterruptedException
C
CyC2018 已提交
263

C
CyC2018 已提交
264
通过调用一个线程的 interrupt() 来中断该线程,如果该线程处于阻塞、限期等待或者无限期等待状态,那么就会抛出 InterruptedException,从而提前结束该线程。但是不能中断 I/O 阻塞和 synchronized 锁阻塞。
C
CyC2018 已提交
265

C
CyC2018 已提交
266
对于以下代码,在 main() 中启动一个线程之后再中断它,由于线程中调用了 Thread.sleep() 方法,因此会抛出一个 InterruptedException,从而提前结束线程,不执行之后的语句。
C
CyC2018 已提交
267 268

```java
C
CyC2018 已提交
269
public class InterruptExample {
C
CyC2018 已提交
270

C
CyC2018 已提交
271 272 273 274 275 276 277 278 279 280 281
    private static class MyThread1 extends Thread {
        @Override
        public void run() {
            try {
                Thread.sleep(2000);
                System.out.println("Thread run");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
C
CyC2018 已提交
282
}
C
CyC2018 已提交
283
```
C
CyC2018 已提交
284

C
CyC2018 已提交
285
```java
C
CyC2018 已提交
286 287 288 289 290
public static void main(String[] args) throws InterruptedException {
    Thread thread1 = new MyThread1();
    thread1.start();
    thread1.interrupt();
    System.out.println("Main run");
C
CyC2018 已提交
291
}
C
CyC2018 已提交
292 293 294
```

```html
C
CyC2018 已提交
295 296 297 298 299 300
Main run
java.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)
    at InterruptExample.lambda$main$0(InterruptExample.java:5)
    at InterruptExample$$Lambda$1/713338599.run(Unknown Source)
    at java.lang.Thread.run(Thread.java:745)
C
CyC2018 已提交
301 302
```

C
CyC2018 已提交
303
## interrupted()
C
CyC2018 已提交
304

C
CyC2018 已提交
305
如果一个线程的 run() 方法执行一个无限循环,并且没有执行 sleep() 等会抛出 InterruptedException 的操作,那么调用线程的 interrupt() 方法就无法使线程提前结束。
C
CyC2018 已提交
306

C
CyC2018 已提交
307
但是调用 interrupt() 方法会设置线程的中断标记,此时调用 interrupted() 方法会返回 true。因此可以在循环体中使用 interrupted() 方法来判断线程是否处于中断状态,从而提前结束线程。
C
CyC2018 已提交
308 309

```java
C
CyC2018 已提交
310
public class InterruptExample {
C
CyC2018 已提交
311

C
CyC2018 已提交
312 313 314 315 316 317 318 319 320
    private static class MyThread2 extends Thread {
        @Override
        public void run() {
            while (!interrupted()) {
                // ..
            }
            System.out.println("Thread end");
        }
    }
C
CyC2018 已提交
321 322 323
}
```

C
CyC2018 已提交
324
```java
C
CyC2018 已提交
325 326 327 328
public static void main(String[] args) throws InterruptedException {
    Thread thread2 = new MyThread2();
    thread2.start();
    thread2.interrupt();
C
CyC2018 已提交
329 330 331
}
```

C
CyC2018 已提交
332
```html
C
CyC2018 已提交
333
Thread end
C
CyC2018 已提交
334 335
```

C
CyC2018 已提交
336
## Executor 的中断操作
C
CyC2018 已提交
337

C
CyC2018 已提交
338
调用 Executor 的 shutdown() 方法会等待线程都执行完毕之后再关闭,但是如果调用的是 shutdownNow() 方法,则相当于调用每个线程的 interrupt() 方法。
C
CyC2018 已提交
339

C
CyC2018 已提交
340
以下使用 Lambda 创建线程,相当于创建了一个匿名内部线程。
C
CyC2018 已提交
341 342

```java
C
CyC2018 已提交
343 344 345 346 347 348 349 350 351 352 353 354
public static void main(String[] args) {
    ExecutorService executorService = Executors.newCachedThreadPool();
    executorService.execute(() -> {
        try {
            Thread.sleep(2000);
            System.out.println("Thread run");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    });
    executorService.shutdownNow();
    System.out.println("Main run");
C
CyC2018 已提交
355 356 357 358
}
```

```html
C
CyC2018 已提交
359 360 361 362 363 364 365 366
Main run
java.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)
    at ExecutorInterruptExample.lambda$main$0(ExecutorInterruptExample.java:9)
    at ExecutorInterruptExample$$Lambda$1/1160460865.run(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)
C
CyC2018 已提交
367 368
```

C
CyC2018 已提交
369
如果只想中断 Executor 中的一个线程,可以通过使用 submit() 方法来提交一个线程,它会返回一个 Future<?> 对象,通过调用该对象的 cancel(true) 方法就可以中断线程。
C
CyC2018 已提交
370 371

```java
C
CyC2018 已提交
372 373
Future<?> future = executorService.submit(() -> {
    // ..
C
CyC2018 已提交
374 375 376 377
});
future.cancel(true);
```

C
CyC2018 已提交
378
# 五、互斥同步
C
CyC2018 已提交
379

C
CyC2018 已提交
380
Java 提供了两种锁机制来控制多个线程对共享资源的互斥访问,第一个是 JVM 实现的 synchronized,而另一个是 JDK 实现的 ReentrantLock。
C
CyC2018 已提交
381

C
CyC2018 已提交
382
## synchronized
C
CyC2018 已提交
383

C
CyC2018 已提交
384
**1. 同步一个代码块** 
C
CyC2018 已提交
385 386

```java
C
CyC2018 已提交
387
public void func() {
C
CyC2018 已提交
388 389 390
    synchronized (this) {
        // ...
    }
C
CyC2018 已提交
391 392 393 394 395
}
```

它只作用于同一个对象,如果调用两个对象上的同步代码块,就不会进行同步。

C
CyC2018 已提交
396
对于以下代码,使用 ExecutorService 执行了两个线程,由于调用的是同一个对象的同步代码块,因此这两个线程会进行同步,当一个线程进入同步语句块时,另一个线程就必须等待。
C
CyC2018 已提交
397 398

```java
C
CyC2018 已提交
399
public class SynchronizedExample {
C
CyC2018 已提交
400

C
CyC2018 已提交
401 402 403 404 405 406 407
    public void func1() {
        synchronized (this) {
            for (int i = 0; i < 10; i++) {
                System.out.print(i + " ");
            }
        }
    }
C
CyC2018 已提交
408 409
}
```
C
CyC2018 已提交
410

C
CyC2018 已提交
411
```java
C
CyC2018 已提交
412 413 414 415 416
public static void main(String[] args) {
    SynchronizedExample e1 = new SynchronizedExample();
    ExecutorService executorService = Executors.newCachedThreadPool();
    executorService.execute(() -> e1.func1());
    executorService.execute(() -> e1.func1());
C
CyC2018 已提交
417 418 419 420
}
```

```html
C
CyC2018 已提交
421
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9
C
CyC2018 已提交
422 423 424 425 426
```

对于以下代码,两个线程调用了不同对象的同步代码块,因此这两个线程就不需要同步。从输出结果可以看出,两个线程交叉执行。

```java
C
CyC2018 已提交
427 428 429 430 431 432
public static void main(String[] args) {
    SynchronizedExample e1 = new SynchronizedExample();
    SynchronizedExample e2 = new SynchronizedExample();
    ExecutorService executorService = Executors.newCachedThreadPool();
    executorService.execute(() -> e1.func1());
    executorService.execute(() -> e2.func1());
C
CyC2018 已提交
433 434 435 436
}
```

```html
C
CyC2018 已提交
437
0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9
C
CyC2018 已提交
438 439 440
```


C
CyC2018 已提交
441
**2. 同步一个方法** 
C
CyC2018 已提交
442 443

```java
C
CyC2018 已提交
444 445
public synchronized void func () {
    // ...
C
CyC2018 已提交
446 447 448
}
```

C
CyC2018 已提交
449
它和同步代码块一样,作用于同一个对象。
C
CyC2018 已提交
450

C
CyC2018 已提交
451
**3. 同步一个类** 
C
CyC2018 已提交
452 453

```java
C
CyC2018 已提交
454 455 456 457
public void func() {
    synchronized (SynchronizedExample.class) {
        // ...
    }
C
CyC2018 已提交
458 459 460
}
```

C
CyC2018 已提交
461
作用于整个类,也就是说两个线程调用同一个类的不同对象上的这种同步语句,也会进行同步。
C
CyC2018 已提交
462 463

```java
C
CyC2018 已提交
464
public class SynchronizedExample {
C
CyC2018 已提交
465

C
CyC2018 已提交
466 467 468 469 470 471 472
    public void func2() {
        synchronized (SynchronizedExample.class) {
            for (int i = 0; i < 10; i++) {
                System.out.print(i + " ");
            }
        }
    }
C
CyC2018 已提交
473 474
}
```
C
CyC2018 已提交
475

C
CyC2018 已提交
476
```java
C
CyC2018 已提交
477 478 479 480 481 482
public static void main(String[] args) {
    SynchronizedExample e1 = new SynchronizedExample();
    SynchronizedExample e2 = new SynchronizedExample();
    ExecutorService executorService = Executors.newCachedThreadPool();
    executorService.execute(() -> e1.func2());
    executorService.execute(() -> e2.func2());
C
CyC2018 已提交
483 484 485 486
}
```

```html
C
CyC2018 已提交
487
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9
C
CyC2018 已提交
488 489
```

C
CyC2018 已提交
490
**4. 同步一个静态方法** 
C
CyC2018 已提交
491 492

```java
C
CyC2018 已提交
493 494
public synchronized static void fun() {
    // ...
C
CyC2018 已提交
495 496 497 498 499
}
```

作用于整个类。

C
CyC2018 已提交
500
## ReentrantLock
C
CyC2018 已提交
501

C
CyC2018 已提交
502
ReentrantLock 是 java.util.concurrent(J.U.C)包中的锁。
C
CyC2018 已提交
503

C
CyC2018 已提交
504
```java
C
CyC2018 已提交
505
public class LockExample {
C
CyC2018 已提交
506

C
CyC2018 已提交
507
    private Lock lock = new ReentrantLock();
C
CyC2018 已提交
508

C
CyC2018 已提交
509 510 511 512 513 514 515 516 517 518
    public void func() {
        lock.lock();
        try {
            for (int i = 0; i < 10; i++) {
                System.out.print(i + " ");
            }
        } finally {
            lock.unlock(); // 确保释放锁,从而避免发生死锁。
        }
    }
C
CyC2018 已提交
519 520 521 522
}
```

```java
C
CyC2018 已提交
523 524 525 526 527
public static void main(String[] args) {
    LockExample lockExample = new LockExample();
    ExecutorService executorService = Executors.newCachedThreadPool();
    executorService.execute(() -> lockExample.func());
    executorService.execute(() -> lockExample.func());
C
CyC2018 已提交
528 529 530 531
}
```

```html
C
CyC2018 已提交
532
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9
C
CyC2018 已提交
533 534 535
```


C
CyC2018 已提交
536
## 比较
C
CyC2018 已提交
537

C
CyC2018 已提交
538
**1. 锁的实现** 
C
CyC2018 已提交
539

C
CyC2018 已提交
540
synchronized 是 JVM 实现的,而 ReentrantLock 是 JDK 实现的。
C
CyC2018 已提交
541

C
CyC2018 已提交
542
**2. 性能** 
C
CyC2018 已提交
543

C
CyC2018 已提交
544
新版本 Java 对 synchronized 进行了很多优化,例如自旋锁等,synchronized 与 ReentrantLock 大致相同。
C
CyC2018 已提交
545

C
CyC2018 已提交
546
**3. 等待可中断** 
C
CyC2018 已提交
547

C
CyC2018 已提交
548
当持有锁的线程长期不释放锁的时候,正在等待的线程可以选择放弃等待,改为处理其他事情。
C
CyC2018 已提交
549

C
CyC2018 已提交
550
ReentrantLock 可中断,而 synchronized 不行。
C
CyC2018 已提交
551

C
CyC2018 已提交
552
**4. 公平锁** 
C
CyC2018 已提交
553

C
CyC2018 已提交
554
公平锁是指多个线程在等待同一个锁时,必须按照申请锁的时间顺序来依次获得锁。
C
CyC2018 已提交
555

C
CyC2018 已提交
556
synchronized 中的锁是非公平的,ReentrantLock 默认情况下也是非公平的,但是也可以是公平的。
C
CyC2018 已提交
557

C
CyC2018 已提交
558
**5. 锁绑定多个条件** 
C
CyC2018 已提交
559

C
CyC2018 已提交
560
一个 ReentrantLock 可以同时绑定多个 Condition 对象。
C
CyC2018 已提交
561

C
CyC2018 已提交
562
## 使用选择
C
CyC2018 已提交
563

C
CyC2018 已提交
564
除非需要使用 ReentrantLock 的高级功能,否则优先使用 synchronized。这是因为 synchronized 是 JVM 实现的一种锁机制,JVM 原生地支持它,而 ReentrantLock 不是所有的 JDK 版本都支持。并且使用 synchronized 不用担心没有释放锁而导致死锁问题,因为 JVM 会确保锁的释放。
C
CyC2018 已提交
565

C
CyC2018 已提交
566
# 六、线程之间的协作
C
CyC2018 已提交
567

C
CyC2018 已提交
568
当多个线程可以一起工作去解决某个问题时,如果某些部分必须在其它部分之前完成,那么就需要对线程进行协调。
C
CyC2018 已提交
569

C
CyC2018 已提交
570
## join()
C
CyC2018 已提交
571

C
CyC2018 已提交
572
在线程中调用另一个线程的 join() 方法,会将当前线程挂起,而不是忙等待,直到目标线程结束。
C
CyC2018 已提交
573

C
CyC2018 已提交
574
对于以下代码,虽然 b 线程先启动,但是因为在 b 线程中调用了 a 线程的 join() 方法,b 线程会等待 a 线程结束才继续执行,因此最后能够保证 a 线程的输出先于 b 线程的输出。
C
CyC2018 已提交
575 576

```java
C
CyC2018 已提交
577
public class JoinExample {
C
CyC2018 已提交
578

C
CyC2018 已提交
579 580 581 582 583 584
    private class A extends Thread {
        @Override
        public void run() {
            System.out.println("A");
        }
    }
C
CyC2018 已提交
585

C
CyC2018 已提交
586
    private class B extends Thread {
C
CyC2018 已提交
587

C
CyC2018 已提交
588
        private A a;
C
CyC2018 已提交
589

C
CyC2018 已提交
590 591 592
        B(A a) {
            this.a = a;
        }
C
CyC2018 已提交
593

C
CyC2018 已提交
594 595 596 597 598 599 600 601 602 603
        @Override
        public void run() {
            try {
                a.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("B");
        }
    }
C
CyC2018 已提交
604

C
CyC2018 已提交
605 606 607 608 609 610
    public void test() {
        A a = new A();
        B b = new B(a);
        b.start();
        a.start();
    }
C
CyC2018 已提交
611 612
}
```
C
CyC2018 已提交
613

C
CyC2018 已提交
614
```java
C
CyC2018 已提交
615 616 617
public static void main(String[] args) {
    JoinExample example = new JoinExample();
    example.test();
C
CyC2018 已提交
618 619 620 621 622 623 624 625
}
```

```
A
B
```

C
CyC2018 已提交
626
## wait() notify() notifyAll()
C
CyC2018 已提交
627

C
CyC2018 已提交
628
调用 wait() 使得线程等待某个条件满足,线程在等待时会被挂起,当其他线程的运行使得这个条件满足时,其它线程会调用 notify() 或者 notifyAll() 来唤醒挂起的线程。
C
CyC2018 已提交
629

C
CyC2018 已提交
630
它们都属于 Object 的一部分,而不属于 Thread。
C
CyC2018 已提交
631

C
CyC2018 已提交
632
只能用在同步方法或者同步控制块中使用,否则会在运行时抛出 IllegalMonitorStateExeception。
C
CyC2018 已提交
633

C
CyC2018 已提交
634
使用 wait() 挂起期间,线程会释放锁。这是因为,如果没有释放锁,那么其它线程就无法进入对象的同步方法或者同步控制块中,那么就无法执行 notify() 或者 notifyAll() 来唤醒挂起的线程,造成死锁。
C
CyC2018 已提交
635 636

```java
C
CyC2018 已提交
637 638 639 640 641
public class WaitNotifyExample {
    public synchronized void before() {
        System.out.println("before");
        notifyAll();
    }
C
CyC2018 已提交
642

C
CyC2018 已提交
643 644 645 646 647 648 649 650
    public synchronized void after() {
        try {
            wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("after");
    }
C
CyC2018 已提交
651 652
}
```
C
CyC2018 已提交
653

C
CyC2018 已提交
654
```java
C
CyC2018 已提交
655 656 657 658 659
public static void main(String[] args) {
    ExecutorService executorService = Executors.newCachedThreadPool();
    WaitNotifyExample example = new WaitNotifyExample();
    executorService.execute(() -> example.after());
    executorService.execute(() -> example.before());
C
CyC2018 已提交
660 661 662 663 664 665 666 667
}
```

```html
before
after
```

C
CyC2018 已提交
668
**wait() 和 sleep() 的区别** 
C
CyC2018 已提交
669

C
CyC2018 已提交
670 671
- wait() 是 Object 的方法,而 sleep() 是 Thread 的静态方法;
- wait() 会释放锁,sleep() 不会。
C
CyC2018 已提交
672

C
CyC2018 已提交
673
## await() signal() signalAll()
C
CyC2018 已提交
674

C
CyC2018 已提交
675
java.util.concurrent 类库中提供了 Condition 类来实现线程之间的协调,可以在 Condition 上调用 await() 方法使线程等待,其它线程调用 signal() 或 signalAll() 方法唤醒等待的线程。相比于 wait() 这种等待方式,await() 可以指定等待的条件,因此更加灵活。
C
CyC2018 已提交
676

C
CyC2018 已提交
677
使用 Lock 来获取一个 Condition 对象。
C
CyC2018 已提交
678 679

```java
C
CyC2018 已提交
680 681 682
public class AwaitSignalExample {
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();
C
CyC2018 已提交
683

C
CyC2018 已提交
684 685 686 687 688 689 690 691 692
    public void before() {
        lock.lock();
        try {
            System.out.println("before");
            condition.signalAll();
        } finally {
            lock.unlock();
        }
    }
C
CyC2018 已提交
693

C
CyC2018 已提交
694 695 696 697 698 699 700 701 702 703 704
    public void after() {
        lock.lock();
        try {
            condition.await();
            System.out.println("after");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
C
CyC2018 已提交
705 706
}
```
C
CyC2018 已提交
707

C
CyC2018 已提交
708
```java
C
CyC2018 已提交
709 710 711 712 713
public static void main(String[] args) {
    ExecutorService executorService = Executors.newCachedThreadPool();
    AwaitSignalExample example = new AwaitSignalExample();
    executorService.execute(() -> example.after());
    executorService.execute(() -> example.before());
C
CyC2018 已提交
714 715 716
}
```

C
CyC2018 已提交
717 718 719 720 721
```html
before
after
```

C
CyC2018 已提交
722
# 七、J.U.C - AQS
C
CyC2018 已提交
723

C
CyC2018 已提交
724
java.util.concurrent(J.U.C)大大提高了并发性能,AQS 被认为是 J.U.C 的核心。
C
CyC2018 已提交
725

C
CyC2018 已提交
726
## CountdownLatch
C
CyC2018 已提交
727 728 729

用来控制一个线程等待多个线程。

C
CyC2018 已提交
730
维护了一个计数器 cnt,每次调用 countDown() 方法会让计数器的值减 1,减到 0 的时候,那些因为调用 await() 方法而在等待的线程就会被唤醒。
C
CyC2018 已提交
731

C
CyC2018 已提交
732
<div align="center"> <img src="../pics//CountdownLatch.png" width=""/> </div><br>
C
CyC2018 已提交
733 734

```java
C
CyC2018 已提交
735
public class CountdownLatchExample {
C
CyC2018 已提交
736

C
CyC2018 已提交
737 738 739 740 741 742 743 744 745 746 747 748 749 750
    public static void main(String[] args) throws InterruptedException {
        final int totalThread = 10;
        CountDownLatch countDownLatch = new CountDownLatch(totalThread);
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < totalThread; i++) {
            executorService.execute(() -> {
                System.out.print("run..");
                countDownLatch.countDown();
            });
        }
        countDownLatch.await();
        System.out.println("end");
        executorService.shutdown();
    }
C
CyC2018 已提交
751 752 753 754 755 756 757
}
```

```html
run..run..run..run..run..run..run..run..run..run..end
```

C
CyC2018 已提交
758
## CyclicBarrier
C
CyC2018 已提交
759 760 761

用来控制多个线程互相等待,只有当多个线程都到达时,这些线程才会继续执行。

C
CyC2018 已提交
762
和 CountdownLatch 相似,都是通过维护计数器来实现的。但是它的计数器是递增的,每次执行 await() 方法之后,计数器会加 1,直到计数器的值和设置的值相等,等待的所有线程才会继续执行。和 CountdownLatch 的另一个区别是,CyclicBarrier 的计数器可以循环使用,所以它才叫做循环屏障。
C
CyC2018 已提交
763 764 765

下图应该从下往上看才正确。

C
CyC2018 已提交
766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788
<div align="center"> <img src="../pics//CyclicBarrier.png" width=""/> </div><br>

```java
public class CyclicBarrierExample {
    public static void main(String[] args) throws InterruptedException {
        final int totalThread = 10;
        CyclicBarrier cyclicBarrier = new CyclicBarrier(totalThread);
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < totalThread; i++) {
            executorService.execute(() -> {
                System.out.print("before..");
                try {
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
                System.out.print("after..");
            });
        }
        executorService.shutdown();
    }
C
CyC2018 已提交
789 790 791 792 793 794 795
}
```

```html
before..before..before..before..before..before..before..before..before..before..after..after..after..after..after..after..after..after..after..after..
```

C
CyC2018 已提交
796
## Semaphore
C
CyC2018 已提交
797

C
CyC2018 已提交
798
Semaphore 就是操作系统中的信号量,可以控制对互斥资源的访问线程数。
C
CyC2018 已提交
799

C
CyC2018 已提交
800
<div align="center"> <img src="../pics//Semaphore.png" width=""/> </div><br>
C
CyC2018 已提交
801

C
CyC2018 已提交
802
以下代码模拟了对某个服务的并发请求,每次只能有 3 个客户端同时访问,请求总数为 10。
C
CyC2018 已提交
803 804

```java
C
CyC2018 已提交
805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824
public class SemaphoreExample {
    public static void main(String[] args) {
        final int clientCount = 3;
        final int totalRequestCount = 10;
        Semaphore semaphore = new Semaphore(clientCount);
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < totalRequestCount; i++) {
            executorService.execute(()->{
                try {
                    semaphore.acquire();
                    System.out.print(semaphore.availablePermits() + " ");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    semaphore.release();
                }
            });
        }
        executorService.shutdown();
    }
C
CyC2018 已提交
825 826 827 828
}
```

```html
C
CyC2018 已提交
829
2 1 2 2 2 2 2 1 2 2
C
CyC2018 已提交
830 831
```

C
CyC2018 已提交
832
# 八、J.U.C - 其它组件
C
CyC2018 已提交
833

C
CyC2018 已提交
834
## FutureTask
C
CyC2018 已提交
835

C
CyC2018 已提交
836
在介绍 Callable 时我们知道它可以有返回值,返回值通过 Future<V> 进行封装。FutureTask 实现了 RunnableFuture 接口,该接口继承自 Runnable 和 Future<V> 接口,这使得 FutureTask 既可以当做一个任务执行,也可以有返回值。
C
CyC2018 已提交
837 838

```java
C
CyC2018 已提交
839
public class FutureTask<V> implements RunnableFuture<V>
C
CyC2018 已提交
840 841 842
```

```java
C
CyC2018 已提交
843
public interface RunnableFuture<V> extends Runnable, Future<V>
C
CyC2018 已提交
844 845
```

C
CyC2018 已提交
846
FutureTask 可用于异步获取执行结果或取消执行任务的场景。当一个计算任务需要执行很长时间,那么就可以用 FutureTask 来封装这个任务,主线程在完成自己的任务之后再去获取结果。
C
CyC2018 已提交
847 848

```java
C
CyC2018 已提交
849 850 851 852 853 854 855 856 857 858 859 860 861
public class FutureTaskExample {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask<Integer> futureTask = new FutureTask<Integer>(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                int result = 0;
                for (int i = 0; i < 100; i++) {
                    Thread.sleep(10);
                    result += i;
                }
                return result;
            }
        });
C
CyC2018 已提交
862

C
CyC2018 已提交
863 864
        Thread computeThread = new Thread(futureTask);
        computeThread.start();
C
CyC2018 已提交
865

C
CyC2018 已提交
866 867 868 869 870 871 872 873 874 875 876
        Thread otherThread = new Thread(() -> {
            System.out.println("other task is running...");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        otherThread.start();
        System.out.println(futureTask.get());
    }
C
CyC2018 已提交
877 878 879 880
}
```

```html
C
CyC2018 已提交
881
other task is running...
C
CyC2018 已提交
882 883 884
4950
```

C
CyC2018 已提交
885
## BlockingQueue
C
CyC2018 已提交
886

C
CyC2018 已提交
887
java.util.concurrent.BlockingQueue 接口有以下阻塞队列的实现:
C
CyC2018 已提交
888

C
CyC2018 已提交
889
-  **FIFO 队列** :LinkedBlockingQueue、ArrayBlockingQueue(固定长度)
C
CyC2018 已提交
890
-  **优先级队列** :PriorityBlockingQueue
C
CyC2018 已提交
891

C
CyC2018 已提交
892
提供了阻塞的 take() 和 put() 方法:如果队列为空 take() 将阻塞,直到队列中有内容;如果队列为满 put() 将阻塞,直到队列有空闲位置。
C
CyC2018 已提交
893

C
CyC2018 已提交
894
**使用 BlockingQueue 实现生产者消费者问题** 
C
CyC2018 已提交
895 896

```java
C
CyC2018 已提交
897
public class ProducerConsumer {
C
CyC2018 已提交
898

C
CyC2018 已提交
899
    private static BlockingQueue<String> queue = new ArrayBlockingQueue<>(5);
C
CyC2018 已提交
900

C
CyC2018 已提交
901 902 903 904 905 906 907 908 909 910 911
    private static class Producer extends Thread {
        @Override
        public void run() {
            try {
                queue.put("product");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.print("produce..");
        }
    }
C
CyC2018 已提交
912

C
CyC2018 已提交
913
    private static class Consumer extends Thread {
C
CyC2018 已提交
914

C
CyC2018 已提交
915 916 917 918 919 920 921 922 923 924
        @Override
        public void run() {
            try {
                String product = queue.take();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.print("consume..");
        }
    }
C
CyC2018 已提交
925 926
}
```
C
CyC2018 已提交
927

C
CyC2018 已提交
928
```java
C
CyC2018 已提交
929 930 931 932 933 934 935 936 937 938 939 940 941
public static void main(String[] args) {
    for (int i = 0; i < 2; i++) {
        Producer producer = new Producer();
        producer.start();
    }
    for (int i = 0; i < 5; i++) {
        Consumer consumer = new Consumer();
        consumer.start();
    }
    for (int i = 0; i < 3; i++) {
        Producer producer = new Producer();
        producer.start();
    }
C
CyC2018 已提交
942 943 944 945
}
```

```html
C
CyC2018 已提交
946
produce..produce..consume..consume..produce..consume..produce..consume..produce..consume..
C
CyC2018 已提交
947 948
```

C
CyC2018 已提交
949 950 951 952 953 954
## ForkJoin

主要用于并行计算中,和 MapReduce 原理类似,都是把大的计算任务拆分成多个小任务并行计算。

```java
public class ForkJoinExample extends RecursiveTask<Integer> {
C
CyC2018 已提交
955
    private final int threshold = 5;
C
CyC2018 已提交
956 957 958 959 960 961 962 963 964 965 966
    private int first;
    private int last;

    public ForkJoinExample(int first, int last) {
        this.first = first;
        this.last = last;
    }

    @Override
    protected Integer compute() {
        int result = 0;
C
CyC2018 已提交
967
        if (last - first <= threshold) {
C
CyC2018 已提交
968 969 970 971 972 973 974 975 976 977 978 979 980 981 982
            // 任务足够小则直接计算
            for (int i = first; i <= last; i++) {
                result += i;
            }
        } else {
            // 拆分成小任务
            int middle = first + (last - first) / 2;
            ForkJoinExample leftTask = new ForkJoinExample(first, middle);
            ForkJoinExample rightTask = new ForkJoinExample(middle + 1, last);
            leftTask.fork();
            rightTask.fork();
            result = leftTask.join() + rightTask.join();
        }
        return result;
    }
C
CyC2018 已提交
983 984 985 986
}
```

```java
C
CyC2018 已提交
987 988 989 990 991
public static void main(String[] args) throws ExecutionException, InterruptedException {
    ForkJoinExample example = new ForkJoinExample(1, 10000);
    ForkJoinPool forkJoinPool = new ForkJoinPool();
    Future result = forkJoinPool.submit(example);
    System.out.println(result.get());
C
CyC2018 已提交
992 993 994
}
```

C
CyC2018 已提交
995
ForkJoin 使用 ForkJoinPool 来启动,它是一个特殊的线程池,线程数量取决于 CPU 核数。
C
CyC2018 已提交
996 997

```java
C
CyC2018 已提交
998
public class ForkJoinPool extends AbstractExecutorService
C
CyC2018 已提交
999 1000
```

C
CyC2018 已提交
1001
ForkJoinPool 实现了工作窃取算法来提高 CPU 的利用率。每个线程都维护了一个双端队列,用来存储需要执行的任务。工作窃取算法允许空闲的线程从其它线程的双端队列中窃取一个任务来执行。窃取的任务必须是最晚的任务,避免和队列所属线程发生竞争。例如下图中,Thread2 从 Thread1 的队列中拿出最晚的 Task1 任务,Thread1 会拿出 Task2 来执行,这样就避免发生竞争。但是如果队列中只有一个任务时还是会发生竞争。
C
CyC2018 已提交
1002

C
CyC2018 已提交
1003
<div align="center"> <img src="../pics//15b45dc6-27aa-4519-9194-f4acfa2b077f.jpg" width=""/> </div><br>
C
CyC2018 已提交
1004

C
CyC2018 已提交
1005
# 九、线程不安全示例
C
CyC2018 已提交
1006 1007 1008

如果多个线程对同一个共享数据进行访问而不采取同步操作的话,那么操作的结果是不一致的。

C
CyC2018 已提交
1009
以下代码演示了 1000 个线程同时对 cnt 执行自增操作,操作结束之后它的值为 997 而不是 1000。
C
CyC2018 已提交
1010 1011

```java
C
CyC2018 已提交
1012
public class ThreadUnsafeExample {
C
CyC2018 已提交
1013

C
CyC2018 已提交
1014
    private int cnt = 0;
C
CyC2018 已提交
1015

C
CyC2018 已提交
1016 1017 1018
    public void add() {
        cnt++;
    }
C
CyC2018 已提交
1019

C
CyC2018 已提交
1020 1021 1022
    public int get() {
        return cnt;
    }
C
CyC2018 已提交
1023 1024
}
```
C
CyC2018 已提交
1025

C
CyC2018 已提交
1026
```java
C
CyC2018 已提交
1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040
public static void main(String[] args) throws InterruptedException {
    final int threadSize = 1000;
    ThreadUnsafeExample example = new ThreadUnsafeExample();
    final CountDownLatch countDownLatch = new CountDownLatch(threadSize);
    ExecutorService executorService = Executors.newCachedThreadPool();
    for (int i = 0; i < threadSize; i++) {
        executorService.execute(() -> {
            example.add();
            countDownLatch.countDown();
        });
    }
    countDownLatch.await();
    executorService.shutdown();
    System.out.println(example.get());
C
CyC2018 已提交
1041 1042 1043 1044 1045 1046 1047
}
```

```html
997
```

C
CyC2018 已提交
1048
# 十、Java 内存模型
C
CyC2018 已提交
1049

C
CyC2018 已提交
1050
Java 内存模型试图屏蔽各种硬件和操作系统的内存访问差异,以实现让 Java 程序在各种平台下都能达到一致的内存访问效果。
C
CyC2018 已提交
1051

C
CyC2018 已提交
1052
## 主内存与工作内存
C
CyC2018 已提交
1053 1054 1055

处理器上的寄存器的读写的速度比内存快几个数量级,为了解决这种速度矛盾,在它们之间加入了高速缓存。

C
CyC2018 已提交
1056
加入高速缓存带来了一个新的问题:缓存一致性。如果多个缓存共享同一块主内存区域,那么多个缓存的数据可能会不一致,需要一些协议来解决这个问题。
C
CyC2018 已提交
1057

C
CyC2018 已提交
1058
<div align="center"> <img src="../pics//68778c1b-15ab-4826-99c0-3b4fd38cb9e9.png" width=""/> </div><br>
C
CyC2018 已提交
1059 1060 1061 1062 1063

所有的变量都存储在主内存中,每个线程还有自己的工作内存,工作内存存储在高速缓存或者寄存器中,保存了该线程使用的变量的主内存副本拷贝。

线程只能直接操作工作内存中的变量,不同线程之间的变量值传递需要通过主内存来完成。

C
CyC2018 已提交
1064
<div align="center"> <img src="../pics//47358f87-bc4c-496f-9a90-8d696de94cee.png" width=""/> </div><br>
C
CyC2018 已提交
1065

C
CyC2018 已提交
1066
## 内存间交互操作
C
CyC2018 已提交
1067

C
CyC2018 已提交
1068
Java 内存模型定义了 8 个操作来完成主内存和工作内存的交互操作。
C
CyC2018 已提交
1069

C
CyC2018 已提交
1070
<div align="center"> <img src="../pics//536c6dfd-305a-4b95-b12c-28ca5e8aa043.png" width=""/> </div><br>
C
CyC2018 已提交
1071

C
CyC2018 已提交
1072 1073 1074 1075 1076 1077 1078 1079
- read:把一个变量的值从主内存传输到工作内存中
- load:在 read 之后执行,把 read 得到的值放入工作内存的变量副本中
- use:把工作内存中一个变量的值传递给执行引擎
- assign:把一个从执行引擎接收到的值赋给工作内存的变量
- store:把工作内存的一个变量的值传送到主内存中
- write:在 store 之后执行,把 store 得到的值放入主内存的变量中
- lock:作用于主内存的变量
- unlock
C
CyC2018 已提交
1080

C
CyC2018 已提交
1081
## 内存模型三大特性
C
CyC2018 已提交
1082

C
CyC2018 已提交
1083
### 1. 原子性
C
CyC2018 已提交
1084

C
CyC2018 已提交
1085
Java 内存模型保证了 read、load、use、assign、store、write、lock 和 unlock 操作具有原子性,例如对一个 int 类型的变量执行 assign 赋值操作,这个操作就是原子性的。但是 Java 内存模型允许虚拟机将没有被 volatile 修饰的 64 位数据(long,double)的读写操作划分为两次 32 位的操作来进行,即 load、store、read 和 write 操作可以不具备原子性。
C
CyC2018 已提交
1086

C
CyC2018 已提交
1087
有一个错误认识就是,int 等原子性的变量在多线程环境中不会出现线程安全问题。前面的线程不安全示例代码中,cnt 变量属于 int 类型变量,1000 个线程对它进行自增操作之后,得到的值为 997 而不是 1000。
C
CyC2018 已提交
1088

C
CyC2018 已提交
1089
为了方便讨论,将内存间的交互操作简化为 3 个:load、assign、store。
C
CyC2018 已提交
1090

C
CyC2018 已提交
1091
下图演示了两个线程同时对 cnt 变量进行操作,load、assign、store 这一系列操作整体上看不具备原子性,那么在 T1 修改 cnt 并且还没有将修改后的值写入主内存,T2 依然可以读入该变量的值。可以看出,这两个线程虽然执行了两次自增运算,但是主内存中 cnt 的值最后为 1 而不是 2。因此对 int 类型读写操作满足原子性只是说明 load、assign、store 这些单个操作具备原子性。
C
CyC2018 已提交
1092

C
CyC2018 已提交
1093
<div align="center"> <img src="../pics//ef8eab00-1d5e-4d99-a7c2-d6d68ea7fe92.png" width=""/> </div><br>
C
CyC2018 已提交
1094

C
CyC2018 已提交
1095
AtomicInteger 能保证多个线程修改的原子性。
C
CyC2018 已提交
1096

C
CyC2018 已提交
1097
<div align="center"> <img src="../pics//952afa9a-458b-44ce-bba9-463e60162945.png" width=""/> </div><br>
C
CyC2018 已提交
1098

C
CyC2018 已提交
1099
使用 AtomicInteger 重写之前线程不安全的代码之后得到以下线程安全实现:
C
CyC2018 已提交
1100 1101

```java
C
CyC2018 已提交
1102 1103
public class AtomicExample {
    private AtomicInteger cnt = new AtomicInteger();
C
CyC2018 已提交
1104

C
CyC2018 已提交
1105 1106 1107
    public void add() {
        cnt.incrementAndGet();
    }
C
CyC2018 已提交
1108

C
CyC2018 已提交
1109 1110 1111
    public int get() {
        return cnt.get();
    }
C
CyC2018 已提交
1112 1113
}
```
C
CyC2018 已提交
1114

C
CyC2018 已提交
1115
```java
C
CyC2018 已提交
1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129
public static void main(String[] args) throws InterruptedException {
    final int threadSize = 1000;
    AtomicExample example = new AtomicExample(); // 只修改这条语句
    final CountDownLatch countDownLatch = new CountDownLatch(threadSize);
    ExecutorService executorService = Executors.newCachedThreadPool();
    for (int i = 0; i < threadSize; i++) {
        executorService.execute(() -> {
            example.add();
            countDownLatch.countDown();
        });
    }
    countDownLatch.await();
    executorService.shutdown();
    System.out.println(example.get());
C
CyC2018 已提交
1130 1131 1132 1133 1134 1135 1136
}
```

```html
1000
```

C
CyC2018 已提交
1137
除了使用原子类之外,也可以使用 synchronized 互斥锁来保证操作的原子性。它对应的内存间交互操作为:lock 和 unlock,在虚拟机实现上对应的字节码指令为 monitorenter 和 monitorexit。
C
CyC2018 已提交
1138 1139

```java
C
CyC2018 已提交
1140 1141
public class AtomicSynchronizedExample {
    private int cnt = 0;
C
CyC2018 已提交
1142

C
CyC2018 已提交
1143 1144 1145
    public synchronized void add() {
        cnt++;
    }
C
CyC2018 已提交
1146

C
CyC2018 已提交
1147 1148 1149
    public synchronized int get() {
        return cnt;
    }
C
CyC2018 已提交
1150 1151
}
```
C
CyC2018 已提交
1152

C
CyC2018 已提交
1153
```java
C
CyC2018 已提交
1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167
public static void main(String[] args) throws InterruptedException {
    final int threadSize = 1000;
    AtomicSynchronizedExample example = new AtomicSynchronizedExample();
    final CountDownLatch countDownLatch = new CountDownLatch(threadSize);
    ExecutorService executorService = Executors.newCachedThreadPool();
    for (int i = 0; i < threadSize; i++) {
        executorService.execute(() -> {
            example.add();
            countDownLatch.countDown();
        });
    }
    countDownLatch.await();
    executorService.shutdown();
    System.out.println(example.get());
C
CyC2018 已提交
1168 1169 1170 1171 1172 1173 1174
}
```

```html
1000
```

C
CyC2018 已提交
1175
### 2. 可见性
C
CyC2018 已提交
1176

C
CyC2018 已提交
1177
可见性指当一个线程修改了共享变量的值,其它线程能够立即得知这个修改。Java 内存模型是通过在变量修改后将新值同步回主内存,在变量读取前从主内存刷新变量值来实现可见性的。
C
CyC2018 已提交
1178

C
CyC2018 已提交
1179
主要有有三种实现可见性的方式:
C
CyC2018 已提交
1180

C
CyC2018 已提交
1181 1182 1183 1184 1185
- volatile
- synchronized,对一个变量执行 unlock 操作之前,必须把变量值同步回主内存。
- final,被 final 关键字修饰的字段在构造器中一旦初始化完成,并且没有发生 this 逃逸(其它线程通过 this 引用访问到初始化了一半的对象),那么其它线程就能看见 final 字段的值。

对前面的线程不安全示例中的 cnt 变量使用 volatile 修饰,不能解决线程不安全问题,因为 volatile 并不能保证操作的原子性。
C
CyC2018 已提交
1186

C
CyC2018 已提交
1187
### 3. 有序性
C
CyC2018 已提交
1188 1189 1190

有序性是指:在本线程内观察,所有操作都是有序的。在一个线程观察另一个线程,所有操作都是无序的,无序是因为发生了指令重排序。

C
CyC2018 已提交
1191
在 Java 内存模型中,允许编译器和处理器对指令进行重排序,重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性。
C
CyC2018 已提交
1192

C
CyC2018 已提交
1193
volatile 关键字通过添加内存屏障的方式来禁止指令重排,即重排序时不能把后面的指令放到内存屏障之前。
C
CyC2018 已提交
1194

C
CyC2018 已提交
1195
也可以通过 synchronized 来保证有序性,它保证每个时刻只有一个线程执行同步代码,相当于是让线程顺序执行同步代码。
C
CyC2018 已提交
1196

C
CyC2018 已提交
1197
## 先行发生原则
C
CyC2018 已提交
1198

C
CyC2018 已提交
1199
上面提到了可以用 volatile 和 synchronized 来保证有序性。除此之外,JVM 还规定了先行发生原则,让一个操作无需控制就能先于另一个操作完成。
C
CyC2018 已提交
1200 1201 1202

主要有以下这些原则:

C
CyC2018 已提交
1203
### 1. 单一线程原则
C
CyC2018 已提交
1204

C
CyC2018 已提交
1205
> Single Thread rule
C
CyC2018 已提交
1206 1207 1208

在一个线程内,在程序前面的操作先行发生于后面的操作。

C
CyC2018 已提交
1209
<div align="center"> <img src="../pics//single-thread-rule.png" width=""/> </div><br>
C
CyC2018 已提交
1210

C
CyC2018 已提交
1211
### 2. 管程锁定规则
C
CyC2018 已提交
1212

C
CyC2018 已提交
1213
> Monitor Lock Rule
C
CyC2018 已提交
1214

C
CyC2018 已提交
1215
一个 unlock 操作先行发生于后面对同一个锁的 lock 操作。
C
CyC2018 已提交
1216

C
CyC2018 已提交
1217
<div align="center"> <img src="../pics//monitor-lock-rule.png" width=""/> </div><br>
C
CyC2018 已提交
1218

C
CyC2018 已提交
1219
### 3. volatile 变量规则
C
CyC2018 已提交
1220

C
CyC2018 已提交
1221
> Volatile Variable Rule
C
CyC2018 已提交
1222

C
CyC2018 已提交
1223
对一个 volatile 变量的写操作先行发生于后面对这个变量的读操作。
C
CyC2018 已提交
1224

C
CyC2018 已提交
1225
<div align="center"> <img src="../pics//volatile-variable-rule.png" width=""/> </div><br>
C
CyC2018 已提交
1226

C
CyC2018 已提交
1227
### 4. 线程启动规则
C
CyC2018 已提交
1228

C
CyC2018 已提交
1229
> Thread Start Rule
C
CyC2018 已提交
1230

C
CyC2018 已提交
1231
Thread 对象的 start() 方法调用先行发生于此线程的每一个动作。
C
CyC2018 已提交
1232

C
CyC2018 已提交
1233
<div align="center"> <img src="../pics//thread-start-rule.png" width=""/> </div><br>
C
CyC2018 已提交
1234

C
CyC2018 已提交
1235
### 5. 线程加入规则
C
CyC2018 已提交
1236

C
CyC2018 已提交
1237
> Thread Join Rule
C
CyC2018 已提交
1238

C
CyC2018 已提交
1239
Thread 对象的结束先行发生于 join() 方法返回。
C
CyC2018 已提交
1240

C
CyC2018 已提交
1241
<div align="center"> <img src="../pics//thread-join-rule.png" width=""/> </div><br>
C
CyC2018 已提交
1242

C
CyC2018 已提交
1243
### 6. 线程中断规则
C
CyC2018 已提交
1244

C
CyC2018 已提交
1245
> Thread Interruption Rule
C
CyC2018 已提交
1246

C
CyC2018 已提交
1247
对线程 interrupt() 方法的调用先行发生于被中断线程的代码检测到中断事件的发生,可以通过 interrupted() 方法检测到是否有中断发生。
C
CyC2018 已提交
1248

C
CyC2018 已提交
1249
### 7. 对象终结规则
C
CyC2018 已提交
1250

C
CyC2018 已提交
1251
> Finalizer Rule
C
CyC2018 已提交
1252

C
CyC2018 已提交
1253
一个对象的初始化完成(构造函数执行结束)先行发生于它的 finalize() 方法的开始。
C
CyC2018 已提交
1254

C
CyC2018 已提交
1255
### 8. 传递性
C
CyC2018 已提交
1256

C
CyC2018 已提交
1257
> Transitivity
C
CyC2018 已提交
1258

C
CyC2018 已提交
1259
如果操作 A 先行发生于操作 B,操作 B 先行发生于操作 C,那么操作 A 先行发生于操作 C。
C
CyC2018 已提交
1260

C
CyC2018 已提交
1261
# 十一、线程安全
C
CyC2018 已提交
1262

C
CyC2018 已提交
1263
## 线程安全定义
C
CyC2018 已提交
1264

D
dingqy 已提交
1265
一个类在可以被多个线程安全调用时就是线程安全的。
C
CyC2018 已提交
1266

C
CyC2018 已提交
1267
## 线程安全分类
C
CyC2018 已提交
1268

C
CyC2018 已提交
1269 1270
线程安全不是一个非真即假的命题,可以将共享数据按照安全程度的强弱顺序分成以下五类:不可变、绝对线程安全、相对线程安全、线程兼容和线程对立。

C
CyC2018 已提交
1271
### 1. 不可变
C
CyC2018 已提交
1272 1273 1274 1275 1276

不可变(Immutable)的对象一定是线程安全的,无论是对象的方法实现还是方法的调用者,都不需要再采取任何的线程安全保障措施,只要一个不可变的对象被正确地构建出来,那其外部的可见状态永远也不会改变,永远也不会看到它在多个线程之中处于不一致的状态。

不可变的类型:

C
CyC2018 已提交
1277 1278 1279 1280
- final 关键字修饰的基本数据类型;
- String
- 枚举类型
- Number 部分子类,如 Long 和 Double 等数值包装类型,BigInteger 和 BigDecimal 等大数据类型。但同为 Number 的子类型的原子类 AtomicInteger 和 AtomicLong 则并非不可变的。
C
CyC2018 已提交
1281

C
CyC2018 已提交
1282
对于集合类型,可以使用 Collections.unmodifiableXXX() 方法来获取一个不可变的集合。
C
CyC2018 已提交
1283 1284

```java
C
CyC2018 已提交
1285 1286 1287 1288 1289 1290
public class ImmutableExample {
    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<>();
        Map<String, Integer> unmodifiableMap = Collections.unmodifiableMap(map);
        unmodifiableMap.put("a", 1);
    }
C
CyC2018 已提交
1291 1292 1293 1294
}
```

```html
C
CyC2018 已提交
1295 1296 1297
Exception in thread "main" java.lang.UnsupportedOperationException
    at java.util.Collections$UnmodifiableMap.put(Collections.java:1457)
    at ImmutableExample.main(ImmutableExample.java:9)
C
CyC2018 已提交
1298 1299
```

C
CyC2018 已提交
1300
Collections.unmodifiableXXX() 先对原始的集合进行拷贝,需要对集合进行修改的方法都直接抛出异常。
C
CyC2018 已提交
1301 1302

```java
C
CyC2018 已提交
1303 1304
public V put(K key, V value) {
    throw new UnsupportedOperationException();
C
CyC2018 已提交
1305 1306 1307
}
```

C
CyC2018 已提交
1308
多线程环境下,应当尽量使对象成为不可变,来满足线程安全。
C
CyC2018 已提交
1309

C
CyC2018 已提交
1310
### 2. 绝对线程安全
C
CyC2018 已提交
1311 1312 1313

不管运行时环境如何,调用者都不需要任何额外的同步措施。

C
CyC2018 已提交
1314
### 3. 相对线程安全
C
CyC2018 已提交
1315 1316 1317

相对的线程安全需要保证对这个对象单独的操作是线程安全的,在调用的时候不需要做额外的保障措施,但是对于一些特定顺序的连续调用,就可能需要在调用端使用额外的同步手段来保证调用的正确性。

C
CyC2018 已提交
1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344
在 Java 语言中,大部分的线程安全类都属于这种类型,例如 Vector、HashTable、Collections 的 synchronizedCollection() 方法包装的集合等。

对于下面的代码,如果删除元素的线程删除了一个元素,而获取元素的线程试图访问一个已经被删除的元素,那么就会抛出 ArrayIndexOutOfBoundsException。

```java
public class VectorUnsafeExample {
    private static Vector<Integer> vector = new Vector<>();

    public static void main(String[] args) {
        while (true) {
            for (int i = 0; i < 100; i++) {
                vector.add(i);
            }
            ExecutorService executorService = Executors.newCachedThreadPool();
            executorService.execute(() -> {
                for (int i = 0; i < vector.size(); i++) {
                    vector.remove(i);
                }
            });
            executorService.execute(() -> {
                for (int i = 0; i < vector.size(); i++) {
                    vector.get(i);
                }
            });
            executorService.shutdown();
        }
    }
C
CyC2018 已提交
1345 1346 1347 1348
}
```

```html
C
CyC2018 已提交
1349 1350 1351 1352 1353
Exception in thread "Thread-159738" java.lang.ArrayIndexOutOfBoundsException: Array index out of range: 3
    at java.util.Vector.remove(Vector.java:831)
    at VectorUnsafeExample.lambda$main$0(VectorUnsafeExample.java:14)
    at VectorUnsafeExample$$Lambda$1/713338599.run(Unknown Source)
    at java.lang.Thread.run(Thread.java:745)
C
CyC2018 已提交
1354 1355 1356 1357 1358 1359
```


如果要保证上面的代码能正确执行下去,就需要对删除元素和获取元素的代码进行同步。

```java
C
CyC2018 已提交
1360 1361 1362 1363 1364 1365
executorService.execute(() -> {
    synchronized (vector) {
        for (int i = 0; i < vector.size(); i++) {
            vector.remove(i);
        }
    }
C
CyC2018 已提交
1366
});
C
CyC2018 已提交
1367 1368 1369 1370 1371 1372
executorService.execute(() -> {
    synchronized (vector) {
        for (int i = 0; i < vector.size(); i++) {
            vector.get(i);
        }
    }
C
CyC2018 已提交
1373 1374 1375
});
```

C
CyC2018 已提交
1376
### 4. 线程兼容
C
CyC2018 已提交
1377

C
CyC2018 已提交
1378
线程兼容是指对象本身并不是线程安全的,但是可以通过在调用端正确地使用同步手段来保证对象在并发环境中可以安全地使用,我们平常说一个类不是线程安全的,绝大多数时候指的是这一种情况。Java API 中大部分的类都是属于线程兼容的,如与前面的 Vector 和 HashTable 相对应的集合类 ArrayList 和 HashMap 等。
C
CyC2018 已提交
1379

C
CyC2018 已提交
1380
### 5. 线程对立
C
CyC2018 已提交
1381

C
CyC2018 已提交
1382
线程对立是指无论调用端是否采取了同步措施,都无法在多线程环境中并发使用的代码。由于 Java 语言天生就具备多线程特性,线程对立这种排斥多线程的代码是很少出现的,而且通常都是有害的,应当尽量避免。
C
CyC2018 已提交
1383

C
CyC2018 已提交
1384
## 线程安全的实现方法
C
CyC2018 已提交
1385

C
CyC2018 已提交
1386
### 1. 互斥同步
C
CyC2018 已提交
1387

C
CyC2018 已提交
1388
synchronized 和 ReentrantLock。
C
CyC2018 已提交
1389

C
CyC2018 已提交
1390
### 2. 非阻塞同步
C
CyC2018 已提交
1391

C
CyC2018 已提交
1392
互斥同步最主要的问题就是进行线程阻塞和唤醒所带来的性能问题,因此这种同步也称为阻塞同步。
C
CyC2018 已提交
1393

C
CyC2018 已提交
1394
互斥同步属于一种悲观的并发策略,总是认为只要不去做正确的同步措施,那就肯定会出现问题。论共享数据是否真的会出现竞争,它都要进行加锁(这里讨论的是概念模型,实际上虚拟机会优化掉很大一部分不必要的加锁)、用户态核心态转换、维护锁计数器和检查是否有被阻塞的线程需要唤醒等操作。
C
CyC2018 已提交
1395

C
CyC2018 已提交
1396
随着硬件指令集的发展,我们可以使用基于冲突检测的乐观并发策略:先进行操作,如果没有其它线程争用共享数据,那操作就成功了,否则采取补偿措施(不断地重试,直到成功为止)。这种乐观的并发策略的许多实现都不需要把线程挂起,因此这种同步操作称为非阻塞同步。
C
CyC2018 已提交
1397

C
CyC2018 已提交
1398
乐观锁需要操作和冲突检测这两个步骤具备原子性,这里就不能再使用互斥同步来保证了,只能靠硬件来完成。
C
CyC2018 已提交
1399

C
CyC2018 已提交
1400
硬件支持的原子性操作最典型的是:比较并交换(Compare-and-Swap,CAS)。CAS 指令需要有 3 个操作数,分别是内存地址 V、旧的预期值 A 和新值 B。当执行操作时,只有当 V 的值等于 A,才将 V 的值更新为 B。
C
CyC2018 已提交
1401

C
CyC2018 已提交
1402
J.U.C 包里面的整数原子类 AtomicInteger,其中的 compareAndSet() 和 getAndIncrement() 等方法都使用了 Unsafe 类的 CAS 操作。
C
CyC2018 已提交
1403

C
CyC2018 已提交
1404
以下代码使用了 AtomicInteger 执行了自增的操作。
C
CyC2018 已提交
1405 1406

```java
C
CyC2018 已提交
1407
private AtomicInteger cnt = new AtomicInteger();
C
CyC2018 已提交
1408

C
CyC2018 已提交
1409 1410
public void add() {
    cnt.incrementAndGet();
C
CyC2018 已提交
1411 1412 1413
}
```

C
CyC2018 已提交
1414 1415
以下代码是 incrementAndGet() 的源码,它调用了 unsafe 的 getAndAddInt() 。

C
CyC2018 已提交
1416
```java
C
CyC2018 已提交
1417 1418
public final int incrementAndGet() {
    return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
C
CyC2018 已提交
1419 1420 1421
}
```

C
CyC2018 已提交
1422 1423
以下代码是 getAndAddInt() 源码,var1 指示内存地址,var2 指示旧值,var4 指示操作需要加的数值,这里为 1。通过 getIntVolatile(var1, var2) 得到旧的预期值,通过调用 compareAndSwapInt() 来进行 CAS 比较,如果 var2==var5,那么就更新内存地址为 var1 的变量为 var5+var4。可以看到 getAndAddInt() 在一个循环中进行,发生冲突的做法是不断的进行重试。

C
CyC2018 已提交
1424
```java
C
CyC2018 已提交
1425 1426 1427 1428 1429
public final int getAndAddInt(Object var1, long var2, int var4) {
    int var5;
    do {
        var5 = this.getIntVolatile(var1, var2);
    } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
C
CyC2018 已提交
1430

C
CyC2018 已提交
1431
    return var5;
C
CyC2018 已提交
1432 1433 1434
}
```

C
CyC2018 已提交
1435 1436 1437
ABA :如果一个变量初次读取的时候是 A 值,它的值被改成了 B,后来又被改回为 A,那 CAS 操作就会误认为它从来没有被改变过。

J.U.C 包提供了一个带有标记的原子引用类 AtomicStampedReference 来解决这个问题,它可以通过控制变量值的版本来保证 CAS 的正确性。大部分情况下 ABA 问题不会影响程序并发的正确性,如果需要解决 ABA 问题,改用传统的互斥同步可能会比原子类更高效。
C
CyC2018 已提交
1438

C
CyC2018 已提交
1439
### 3. 无同步方案
C
CyC2018 已提交
1440 1441 1442

要保证线程安全,并不是一定就要进行同步,两者没有因果关系。同步只是保证共享数据争用时的正确性的手段,如果一个方法本来就不涉及共享数据,那它自然就无须任何同步措施去保证正确性,因此会有一些代码天生就是线程安全的。

C
CyC2018 已提交
1443
**(一)可重入代码(Reentrant Code)** 
C
CyC2018 已提交
1444

C
CyC2018 已提交
1445
这种代码也叫做纯代码(Pure Code),可以在代码执行的任何时刻中断它,转而去执行另外一段代码(包括递归调用它本身),而在控制权返回后,原来的程序不会出现任何错误。
C
CyC2018 已提交
1446

C
CyC2018 已提交
1447
可重入代码有一些共同的特征,例如不依赖存储在堆上的数据和公用的系统资源、用到的状态量都由参数中传入、不调用非可重入的方法等。
C
CyC2018 已提交
1448

C
CyC2018 已提交
1449
**(二)栈封闭** 
C
CyC2018 已提交
1450

X
typo  
Xiangmingzhe 已提交
1451
多个线程访问同一个方法的局部变量时,不会出现线程安全问题,因为局部变量存储在栈中,属于线程私有的。
C
CyC2018 已提交
1452 1453

```java
C
CyC2018 已提交
1454 1455
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
C
CyC2018 已提交
1456

C
CyC2018 已提交
1457 1458 1459 1460 1461 1462 1463 1464
public class StackClosedExample {
    public void add100() {
        int cnt = 0;
        for (int i = 0; i < 100; i++) {
            cnt++;
        }
        System.out.println(cnt);
    }
C
CyC2018 已提交
1465 1466
}
```
C
CyC2018 已提交
1467

C
CyC2018 已提交
1468
```java
C
CyC2018 已提交
1469 1470 1471 1472 1473 1474
public static void main(String[] args) {
    StackClosedExample example = new StackClosedExample();
    ExecutorService executorService = Executors.newCachedThreadPool();
    executorService.execute(() -> example.add100());
    executorService.execute(() -> example.add100());
    executorService.shutdown();
C
CyC2018 已提交
1475 1476 1477 1478 1479 1480 1481 1482
}
```

```html
100
100
```

C
CyC2018 已提交
1483
**(三)线程本地存储(Thread Local Storage)** 
C
CyC2018 已提交
1484 1485 1486

如果一段代码中所需要的数据必须与其他代码共享,那就看看这些共享数据的代码是否能保证在同一个线程中执行。如果能保证,我们就可以把共享数据的可见范围限制在同一个线程之内,这样,无须同步也能保证线程之间不出现数据争用的问题。

C
CyC2018 已提交
1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513
符合这种特点的应用并不少见,大部分使用消费队列的架构模式(如“生产者-消费者”模式)都会将产品的消费过程尽量在一个线程中消费完,其中最重要的一个应用实例就是经典 Web 交互模型中的“一个请求对应一个服务器线程”(Thread-per-Request)的处理方式,这种处理方式的广泛应用使得很多 Web 服务端应用都可以使用线程本地存储来解决线程安全问题。

可以使用 java.lang.ThreadLocal 类来实现线程本地存储功能。

对于以下代码,thread1 中设置 threadLocal 为 1,而 thread2 设置 threadLocal 为 2。过了一段时间之后,thread1 读取 threadLocal 依然是 1,不受 thread2 的影响。

```java
public class ThreadLocalExample {
    public static void main(String[] args) {
        ThreadLocal threadLocal = new ThreadLocal();
        Thread thread1 = new Thread(() -> {
            threadLocal.set(1);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(threadLocal.get());
            threadLocal.remove();
        });
        Thread thread2 = new Thread(() -> {
            threadLocal.set(2);
            threadLocal.remove();
        });
        thread1.start();
        thread2.start();
    }
C
CyC2018 已提交
1514 1515 1516 1517 1518 1519 1520
}
```

```html
1
```

C
CyC2018 已提交
1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538
为了理解 ThreadLocal,先看以下代码:

```java
public class ThreadLocalExample1 {
    public static void main(String[] args) {
        ThreadLocal threadLocal1 = new ThreadLocal();
        ThreadLocal threadLocal2 = new ThreadLocal();
        Thread thread1 = new Thread(() -> {
            threadLocal1.set(1);
            threadLocal2.set(1);
        });
        Thread thread2 = new Thread(() -> {
            threadLocal1.set(2);
            threadLocal2.set(2);
        });
        thread1.start();
        thread2.start();
    }
C
CyC2018 已提交
1539 1540 1541 1542 1543
}
```

它所对应的底层结构图为:

C
CyC2018 已提交
1544
<div align="center"> <img src="../pics//3646544a-cb57-451d-9e03-d3c4f5e4434a.png" width=""/> </div><br>
C
CyC2018 已提交
1545

C
CyC2018 已提交
1546
每个 Thread 都有一个 ThreadLocal.ThreadLocalMap 对象,Thread 类中就定义了 ThreadLocal.ThreadLocalMap 成员。
C
CyC2018 已提交
1547 1548

```java
C
CyC2018 已提交
1549 1550 1551
/* ThreadLocal values pertaining to this thread. This map is maintained
 * by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
C
CyC2018 已提交
1552 1553
```

C
CyC2018 已提交
1554
当调用一个 ThreadLocal 的 set(T value) 方法时,先得到当前线程的 ThreadLocalMap 对象,然后将 ThreadLocal->value 键值对插入到该 Map 中。
C
CyC2018 已提交
1555 1556

```java
C
CyC2018 已提交
1557 1558 1559 1560 1561 1562 1563
public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
C
CyC2018 已提交
1564 1565 1566
}
```

C
CyC2018 已提交
1567
get() 方法类似。
C
CyC2018 已提交
1568 1569

```java
C
CyC2018 已提交
1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581
public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
C
CyC2018 已提交
1582 1583 1584
}
```

C
CyC2018 已提交
1585
ThreadLocal 从理论上讲并不是用来解决多线程并发问题的,因为根本不存在多线程竞争。在一些场景 (尤其是使用线程池) 下,由于 ThreadLocal.ThreadLocalMap 的底层数据结构导致 ThreadLocal 有内存泄漏的情况,尽可能在每次使用 ThreadLocal 后手动调用 remove(),以避免出现 ThreadLocal 经典的内存泄漏甚至是造成自身业务混乱的风险。
C
CyC2018 已提交
1586

C
CyC2018 已提交
1587
# 十二、锁优化
C
CyC2018 已提交
1588

C
CyC2018 已提交
1589
这里的锁优化主要是指虚拟机对 synchronized 的优化。
C
CyC2018 已提交
1590

C
CyC2018 已提交
1591
## 自旋锁
C
CyC2018 已提交
1592

C
CyC2018 已提交
1593
互斥同步的进入阻塞状态的开销都很大,应该尽量避免。在许多应用中,共享数据的锁定状态只会持续很短的一段时间。自旋锁的思想是让一个线程在请求一个共享数据的锁时执行忙循环(自旋)一段时间,如果在这段时间内能获得锁,就可以避免进入阻塞状态。
C
CyC2018 已提交
1594

C
CyC2018 已提交
1595
自选锁虽然能避免进入阻塞状态从而减少开销,但是它需要进行忙循环操作占用 CPU 时间,它只适用于共享数据的锁定状态很短的场景。
C
CyC2018 已提交
1596

C
CyC2018 已提交
1597
在 JDK 1.6 中引入了自适应的自旋锁。自适应意味着自旋的次数不再固定了,而是由前一次在同一个锁上的自旋次数及锁的拥有者的状态来决定。
C
CyC2018 已提交
1598

C
CyC2018 已提交
1599
## 锁消除
C
CyC2018 已提交
1600

C
CyC2018 已提交
1601
锁消除是指对于被检测出不可能存在竞争的共享数据的锁进行消除。
C
CyC2018 已提交
1602

C
CyC2018 已提交
1603
锁消除主要是通过逃逸分析来支持,如果堆上的共享数据不可能逃逸出去被其它线程访问到,那么就可以把它们当成私有数据对待,也就可以将它们的锁进行消除。
C
CyC2018 已提交
1604 1605

对于一些看起来没有加锁的代码,其实隐式的加了很多锁。例如下面的字符串拼接代码就隐式加了锁:
C
CyC2018 已提交
1606 1607

```java
C
CyC2018 已提交
1608 1609
public static String concatString(String s1, String s2, String s3) {
    return s1 + s2 + s3;
C
CyC2018 已提交
1610 1611 1612
}
```

C
CyC2018 已提交
1613
String 是一个不可变的类,编译器会对 String 的拼接自动优化。在 JDK 1.5 之前,会转化为 StringBuffer 对象的连续 append() 操作:
C
CyC2018 已提交
1614 1615

```java
C
CyC2018 已提交
1616 1617 1618 1619 1620 1621
public static String concatString(String s1, String s2, String s3) {
    StringBuffer sb = new StringBuffer();
    sb.append(s1);
    sb.append(s2);
    sb.append(s3);
    return sb.toString();
C
CyC2018 已提交
1622 1623 1624
}
```

C
CyC2018 已提交
1625
每个 append() 方法中都有一个同步块。虚拟机观察变量 sb,很快就会发现它的动态作用域被限制在 concatString() 方法内部。也就是说,sb 的所有引用永远不会“逃逸”到 concatString() 方法之外,其他线程无法访问到它,因此可以进行消除。
C
CyC2018 已提交
1626

C
CyC2018 已提交
1627
## 锁粗化
C
CyC2018 已提交
1628

C
CyC2018 已提交
1629
如果一系列的连续操作都对同一个对象反复加锁和解锁,频繁的加锁操作就会导致性能损耗。
C
CyC2018 已提交
1630

C
CyC2018 已提交
1631
上一节的示例代码中连续的 append() 方法就属于这类情况。如果虚拟机探测到由这样的一串零碎的操作都对同一个对象加锁,将会把加锁的范围扩展(粗化)到整个操作序列的外部。对于上一节的示例代码就是扩展到第一个 append() 操作之前直至最后一个 append() 操作之后,这样只需要加锁一次就可以了。
C
CyC2018 已提交
1632

C
CyC2018 已提交
1633
## 轻量级锁
C
CyC2018 已提交
1634

C
CyC2018 已提交
1635
JDK 1.6 引入了偏向锁和轻量级锁,从而让锁拥有了四个状态:无锁状态(unlocked)、偏向锁状态(biasble)、轻量级锁状态(lightweight locked)和重量级锁状态(inflated)。
C
CyC2018 已提交
1636

C
CyC2018 已提交
1637
以下是 HotSpot 虚拟机对象头的内存布局,这些数据被称为 mark word。其中 tag bits 对应了五个状态,这些状态在右侧的 state 表格中给出,应该注意的是 state 表格不是存储在对象头中的。除了 marked for gc 状态,其它四个状态已经在前面介绍过了。
C
CyC2018 已提交
1638

C
CyC2018 已提交
1639
<div align="center"> <img src="../pics//bb6a49be-00f2-4f27-a0ce-4ed764bc605c.png" width="600"/> </div><br>
C
CyC2018 已提交
1640

C
CyC2018 已提交
1641
下图左侧是一个线程的虚拟机栈,其中有一部分称为 Lock Record 的区域,这是在轻量级锁运行过程创建的,用于存放锁对象的 Mark Word。而右侧就是一个锁对象,包含了 Mark Word 和其它信息。
C
CyC2018 已提交
1642

C
CyC2018 已提交
1643
<div align="center"> <img src="../pics//051e436c-0e46-4c59-8f67-52d89d656182.png" width="500"/> </div><br>
C
CyC2018 已提交
1644

C
CyC2018 已提交
1645
轻量级锁是相对于传统的重量级锁而言,它使用 CAS 操作来避免重量级锁使用互斥量的开销。对于绝大部分的锁,在整个同步周期内都是不存在竞争的,因此也就不需要都使用互斥量进行同步,可以先采用 CAS 操作进行同步,如果 CAS 失败了再改用互斥量进行同步。
C
CyC2018 已提交
1646

C
CyC2018 已提交
1647
当尝试获取一个锁对象时,如果锁对象标记为 0 01,说明锁对象的锁未锁定(unlocked)状态。此时虚拟机在当前线程栈中创建 Lock Record,然后使用 CAS 操作将对象的 Mark Word 更新为 Lock Record 指针。如果 CAS 操作成功了,那么线程就获取了该对象上的锁,并且对象的 Mark Word 的锁标记变为 00,表示该对象处于轻量级锁状态。
C
CyC2018 已提交
1648

C
CyC2018 已提交
1649
<div align="center"> <img src="../pics//baaa681f-7c52-4198-a5ae-303b9386cf47.png" width="500"/> </div><br>
C
CyC2018 已提交
1650

C
CyC2018 已提交
1651
如果 CAS 操作失败了,虚拟机首先会检查对象的 Mark Word 是否指向当前线程的虚拟机栈,如果是的话说明当前线程已经拥有了这个锁对象,那就可以直接进入同步块继续执行,否则说明这个锁对象已经被其他线程线程抢占了。如果有两条以上的线程争用同一个锁,那轻量级锁就不再有效,要膨胀为重量级锁。
C
CyC2018 已提交
1652

C
CyC2018 已提交
1653
## 偏向锁
C
CyC2018 已提交
1654

C
CyC2018 已提交
1655
偏向锁的思想是偏向于让第一个获取锁对象的线程,这个线程在之后获取该锁就不再需要进行同步操作,甚至连 CAS 操作也不再需要。
C
CyC2018 已提交
1656

C
CyC2018 已提交
1657
当锁对象第一次被线程获得的时候,进入偏向状态,标记为 1 01。同时使用 CAS 操作将线程 ID 记录到 Mark Word 中,如果 CAS 操作成功,这个线程以后每次进入这个锁相关的同步块就不需要再进行任何同步操作。
C
CyC2018 已提交
1658

C
CyC2018 已提交
1659
当有另外一个线程去尝试获取这个锁对象时,偏向状态就宣告结束,此时撤销偏向(Revoke Bias)后恢复到未锁定状态或者轻量级锁状态。
C
CyC2018 已提交
1660

C
CyC2018 已提交
1661
<div align="center"> <img src="../pics//390c913b-5f31-444f-bbdb-2b88b688e7ce.jpg" width="600"/> </div><br>
C
CyC2018 已提交
1662

C
CyC2018 已提交
1663
# 十三、多线程开发良好的实践
C
CyC2018 已提交
1664

C
CyC2018 已提交
1665
- 给线程起个有意义的名字,这样可以方便找 Bug。
C
CyC2018 已提交
1666

C
CyC2018 已提交
1667
- 缩小同步范围,例如对于 synchronized,应该尽量使用同步块而不是同步方法。
C
CyC2018 已提交
1668

C
CyC2018 已提交
1669
- 多用同步类少用 wait() 和 notify()。首先,CountDownLatch, CyclicBarrier, Semaphore 和 Exchanger 这些同步类简化了编码操作,而用 wait() 和 notify() 很难实现对复杂的控制流;其次,这些同步类是由最好的企业编写和维护,在后续的 JDK 中还会不断优化和完善,使用这些更高等级的同步工具你的程序可以不费吹灰之力获得优化。
C
CyC2018 已提交
1670

C
CyC2018 已提交
1671
- 多用并发集合少用同步集合,例如应该使用 ConcurrentHashMap 而不是 Hashtable。
C
CyC2018 已提交
1672

C
CyC2018 已提交
1673
- 使用本地变量和不可变类来保证线程安全。
C
CyC2018 已提交
1674

C
CyC2018 已提交
1675
- 使用线程池而不是直接创建 Thread 对象,这是因为创建线程代价很高,线程池可以有效地利用有限的线程来启动任务。
C
CyC2018 已提交
1676

C
CyC2018 已提交
1677
- 使用 BlockingQueue 实现生产者消费者问题。
C
CyC2018 已提交
1678

C
CyC2018 已提交
1679
# 参考资料
C
CyC2018 已提交
1680

C
CyC2018 已提交
1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695
- BruceEckel. Java 编程思想: 第 4 版 [M]. 机械工业出版社, 2007.
- 周志明. 深入理解 Java 虚拟机 [M]. 机械工业出版社, 2011.
- [Threads and Locks](https://docs.oracle.com/javase/specs/jvms/se6/html/Threads.doc.html)
- [线程通信](http://ifeve.com/thread-signaling/#missed_signal)
- [Java 线程面试题 Top 50](http://www.importnew.com/12773.html)
- [BlockingQueue](http://tutorials.jenkov.com/java-util-concurrent/blockingqueue.html)
- [thread state java](https://stackoverflow.com/questions/11265289/thread-state-java)
- [CSC 456 Spring 2012/ch7 MN](http://wiki.expertiza.ncsu.edu/index.php/CSC_456_Spring_2012/ch7_MN)
- [Java - Understanding Happens-before relationship](https://www.logicbig.com/tutorials/core-java-tutorial/java-multi-threading/happens-before.html)
- [6장 Thread Synchronization](https://www.slideshare.net/novathinker/6-thread-synchronization)
- [How is Java's ThreadLocal implemented under the hood?](https://stackoverflow.com/questions/1202444/how-is-javas-threadlocal-implemented-under-the-hood/15653015)
- [Concurrent](https://sites.google.com/site/webdevelopart/21-compile/06-java/javase/concurrent?tmpl=%2Fsystem%2Fapp%2Ftemplates%2Fprint%2F&showPrintDialog=1)
- [JAVA FORK JOIN EXAMPLE](http://www.javacreed.com/java-fork-join-example/ "Java Fork Join Example")
- [聊聊并发(八)——Fork/Join 框架介绍](http://ifeve.com/talk-concurrency-forkjoin/)
- [Eliminating SynchronizationRelated Atomic Operations with Biased Locking and Bulk Rebiasing](http://www.oracle.com/technetwork/java/javase/tech/biasedlocking-oopsla2006-preso-150106.pdf)