第1章 課程準(zhǔn)備
本章首先從課程重點(diǎn)拇囊、特點(diǎn)圣猎、適合人群及學(xué)習(xí)收獲幾個方面對課程進(jìn)行整體的介紹逝钥,然后會從一個實(shí)際的計數(shù)場景實(shí)現(xiàn)開始,給大家展示多線程并發(fā)時的線程不安全問題糕再,讓大家能夠初體驗(yàn)到并發(fā)編程量没,之后會講解并發(fā)和高并發(fā)的概念,并通過對比讓大家明白到底什么是并發(fā)和高并發(fā)亿鲜,最后會給出課程涉及到的知識技能允蜈,為后續(xù)的學(xué)習(xí)做好準(zhǔn)備。
1-1 課程導(dǎo)學(xué)
-
高并發(fā)解決思路與手段
1-2 并發(fā)編程初體驗(yàn)
多線程計數(shù)不準(zhǔn)。
1-3 并發(fā)與高并發(fā)基本概念
-
并發(fā)
-
高并發(fā)
-
對比
-
基礎(chǔ)知識與核心知識
-
知識技能
- 手記
https://www.imooc.com/article/25277
第2章 并發(fā)基礎(chǔ)
本章主要講解并發(fā)學(xué)習(xí)必須理解的一些基本概念价淌,主要包括CPU多級緩存和Java內(nèi)存模型(JMM)肴焊。其中CPU多級緩存里深入講解了緩存一致性和亂序執(zhí)行優(yōu)化。Java內(nèi)存模型(JMM)里詳細(xì)講解了JMM規(guī)定、JMM抽象結(jié)構(gòu)巾陕、同步的八種操作及同步規(guī)則向叉。這些基本概念對于后面的并發(fā)編程很重要,也屬于面試程ピ矗考點(diǎn),需要認(rèn)真體會掌握熏挎。
2-1 CPU多級緩存-緩存一致性
-
CPU的多級緩存
[圖片上傳中...(image.png-8a8b17-1529669416185-0)]
- 為什么需要CPU緩沖
cpu的頻率太快聋溜,快到主存跟不上谆膳,是為了緩解CPU的內(nèi)存之間的速度不匹配的問題。 -
緩存一致性(MESI)
用于保證多個CPU cache之間緩存共享數(shù)據(jù)的一致性
M: 被修改(Modified)
該緩存行只被緩存在該CPU的緩存中撮躁,并且是被修改過的(dirty)漱病,即與主存中的數(shù)據(jù)不一致,該緩存行中的內(nèi)存需要在未來的某個時間點(diǎn)(允許其它CPU讀取請主存中相應(yīng)內(nèi)存之前)寫回(write back)主存把曼。當(dāng)被寫回主存之后褐鸥,該緩存行的狀態(tài)會變成獨(dú)享(exclusive)狀態(tài)鸿捧。
E: 獨(dú)享的(Exclusive)
該緩存行只被緩存在該CPU的緩存中坷衍,它是未被修改過的(clean)馍佑,與主存中數(shù)據(jù)一致。該狀態(tài)可以在任何時刻當(dāng)有其它CPU讀取該內(nèi)存時變成共享狀態(tài)(shared)叙赚。同樣地老客,當(dāng)CPU修改該緩存行中內(nèi)容時,該狀態(tài)可以變成Modified狀態(tài)震叮。
S: 共享的(Shared)
該狀態(tài)意味著該緩存行可能被多個CPU緩存胧砰,并且各個緩存中的數(shù)據(jù)與主存數(shù)據(jù)一致(clean),當(dāng)有一個CPU修改該緩存行中苇瓣,其它CPU中該緩存行可以被作廢(變成無效狀態(tài)(Invalid))朴则。
I: 無效的(Invalid)
該緩存是無效的(可能有其它CPU修改了該緩存行)。
2-2 CPU多級緩存-亂序執(zhí)行優(yōu)化
-
處理器為類提高運(yùn)算速度而做出違背代碼原有順序的優(yōu)化
在單核的條件下钓简,沒問題,但在多核執(zhí)行的情況下汹想,每個核都可能亂序外邓。
2-3 JAVA內(nèi)存模型(Java Memory Model, JMM)
-
基本模型
Stack: 存放的數(shù)據(jù)大小與生存期是確定的,缺乏靈活性古掏,速度比較快损话,數(shù)據(jù)可已共享,存放基本類型和句柄
Heap: 運(yùn)行時確定大小,生存期也不必事先告訴編譯器丧枪,是在運(yùn)行期在動態(tài)分配內(nèi)存的光涂,垃圾收集器會清理它們,由于動態(tài)分配的內(nèi)存拧烦,速度比較慢
-
cpu寄存器
-
對比
-
抽象結(jié)果圖
線程A把本地內(nèi)存中的共享變量副本刷新到主內(nèi)存里忘闻,線程B從主內(nèi)存中讀取。
-
同步操作與規(guī)則
2-4 并發(fā)的優(yōu)勢與風(fēng)險
第3章 項(xiàng)目準(zhǔn)備
本章主要是為課程里代碼演示做必要的準(zhǔn)備恋博。首先會基于SpringBoot快速搭建一個方便演示的Java項(xiàng)目齐佳,然后簡單介紹一下碼云及代碼的管理。項(xiàng)目搭建好债沮,我會使用簡單的例子演示一下并發(fā)的模擬驗(yàn)證炼吴,主要包括對工具Postman、JMeter疫衩、Apache Bench(AB)的使用硅蹦,以及使用并發(fā)的代碼來驗(yàn)證并發(fā)處理的正確性。
3-1 案例環(huán)境初始化
https://blog.csdn.net/lom9357bye/article/details/69677120
log:@Slf4j
https://www.2cto.com/kf/201712/702543.html
3-2 案例準(zhǔn)備工作
3-3 并發(fā)模擬-工具
3-4 并發(fā)模擬-代碼
第4章 線程安全性
本章講解線程安全性闷煤,主要從原子性童芹、可見性、有序性三個方面進(jìn)行講解曹傀。原子性部分辐脖,會詳細(xì)講解atomic包下相關(guān)類、CAS原理皆愉、Unsafe類嗜价、synchronized關(guān)鍵字等的使用及注意事項(xiàng)∧宦可見性部分久锥,主要介紹的是volatile關(guān)鍵字的規(guī)則和使用,及synchronized關(guān)鍵字的可見性异剥。有序性部分瑟由,則重點(diǎn)講解了happens-before原則。
4-1 線程安全性-原子性-atomic-1
-
基本類
AtomicInteger
@Slf4j
@ThreadSafe
public class CountExample2 {
// 請求總數(shù)
public static int clientTotal = 5000;
// 同時并發(fā)執(zhí)行的線程數(shù)
public static int threadTotal = 200;
public static AtomicInteger count = new AtomicInteger(0);
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newCachedThreadPool();
final Semaphore semaphore = new Semaphore(threadTotal);
final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
for (int i = 0; i < clientTotal ; i++) {
executorService.execute(() -> {
try {
semaphore.acquire();
add();
semaphore.release();
} catch (Exception e) {
log.error("exception", e);
}
countDownLatch.countDown();
});
}
countDownLatch.await();
executorService.shutdown();
log.info("count:{}", count.get());
}
private static void add() {
count.incrementAndGet();
}
}
使用了AtomicInteger這個類冤寿,可以保證原子性歹苦。當(dāng)我們count.incrementAndGet();
的時候,底層使用了這樣的代碼
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));
return var5;
}
基于CAS:Compare and Swap督怜, 翻譯成比較并交換
CAS有3個操作數(shù)殴瘦,內(nèi)存值V,舊的預(yù)期值A(chǔ)号杠,要修改的新值B蚪腋。當(dāng)且僅當(dāng)預(yù)期值A(chǔ)和內(nèi)存值V相同時丰歌,將內(nèi)存值V修改為B,否則什么都不做屉凯。
- LongAdder
public class AtomicExample3 {
// 請求總數(shù)
public static int clientTotal = 5000;
// 同時并發(fā)執(zhí)行的線程數(shù)
public static int threadTotal = 200;
public static LongAdder count = new LongAdder();
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newCachedThreadPool();
final Semaphore semaphore = new Semaphore(threadTotal);
final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
for (int i = 0; i < clientTotal ; i++) {
executorService.execute(() -> {
try {
semaphore.acquire();
add();
semaphore.release();
} catch (Exception e) {
log.error("exception", e);
}
countDownLatch.countDown();
});
}
countDownLatch.await();
executorService.shutdown();
log.info("count:{}", count);
}
private static void add() {
count.increment();
}
}
- AtomicLong和LongAdder對比
- AtomicReference立帖、AtomicReferneceFieldUpdater
這兩個用的比較少 - AtomicStampReference:CAS的ABA問題
ABA問題就是其他線程把A值改為B又改回A,CAS獲得值與期望值相等悠砚,這是就需要再添加一個版本號來控制晓勇。
4-3 線程安全性-原子性-synchronized
- 修飾代碼塊:大括號括起來的代碼,作用于調(diào)用的對象
@Slf4j
public class SynchronizedExample1 {
// 修飾一個代碼塊
public void test1() {
synchronized (this) {
for (int i = 0; i < 10; i++) {
log.info("test1 - {}", i);
}
}
}
// 修飾一個方法
public synchronized void test2() {
for (int i = 0; i < 10; i++) {
log.info("test - {}", i);
}
}
public static void main(String[] args) {
// 聲明一個實(shí)例
SynchronizedExample1 example1 = new SynchronizedExample1();
// 聲明一個線程池
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.execute(()->{
example1.test1();
});
executorService.execute(()->{
example1.test1();
});
}
}
先打印test1-0到9再打印test1-0到9
15:22:05.516 [pool-1-thread-1] INFO com.cuzz.concurrency.example.sync.SynchronizedExample1 - test1 - 0
15:22:05.526 [pool-1-thread-1] INFO com.cuzz.concurrency.example.sync.SynchronizedExample1 - test1 - 1
15:22:05.526 [pool-1-thread-1] INFO com.cuzz.concurrency.example.sync.SynchronizedExample1 - test1 - 2
15:22:05.526 [pool-1-thread-1] INFO com.cuzz.concurrency.example.sync.SynchronizedExample1 - test1 - 3
15:22:05.526 [pool-1-thread-1] INFO com.cuzz.concurrency.example.sync.SynchronizedExample1 - test1 - 4
15:22:05.527 [pool-1-thread-1] INFO com.cuzz.concurrency.example.sync.SynchronizedExample1 - test1 - 5
15:22:05.527 [pool-1-thread-1] INFO com.cuzz.concurrency.example.sync.SynchronizedExample1 - test1 - 6
15:22:05.527 [pool-1-thread-1] INFO com.cuzz.concurrency.example.sync.SynchronizedExample1 - test1 - 7
15:22:05.527 [pool-1-thread-1] INFO com.cuzz.concurrency.example.sync.SynchronizedExample1 - test1 - 8
15:22:05.527 [pool-1-thread-1] INFO com.cuzz.concurrency.example.sync.SynchronizedExample1 - test1 - 9
15:22:05.527 [pool-1-thread-2] INFO com.cuzz.concurrency.example.sync.SynchronizedExample1 - test1 - 0
15:22:05.527 [pool-1-thread-2] INFO com.cuzz.concurrency.example.sync.SynchronizedExample1 - test1 - 1
15:22:05.527 [pool-1-thread-2] INFO com.cuzz.concurrency.example.sync.SynchronizedExample1 - test1 - 2
15:22:05.527 [pool-1-thread-2] INFO com.cuzz.concurrency.example.sync.SynchronizedExample1 - test1 - 3
15:22:05.527 [pool-1-thread-2] INFO com.cuzz.concurrency.example.sync.SynchronizedExample1 - test1 - 4
15:22:05.527 [pool-1-thread-2] INFO com.cuzz.concurrency.example.sync.SynchronizedExample1 - test1 - 5
15:22:05.527 [pool-1-thread-2] INFO com.cuzz.concurrency.example.sync.SynchronizedExample1 - test1 - 6
15:22:05.527 [pool-1-thread-2] INFO com.cuzz.concurrency.example.sync.SynchronizedExample1 - test1 - 7
15:22:05.527 [pool-1-thread-2] INFO com.cuzz.concurrency.example.sync.SynchronizedExample1 - test1 - 8
15:22:05.527 [pool-1-thread-2] INFO com.cuzz.concurrency.example.sync.SynchronizedExample1 - test1 - 9
使用線程池哩簿,他們本來可以同時進(jìn)行的宵蕉,加了synchronized 就同步是使用了同一個對象。
當(dāng)使用多了對象時
@Slf4j
public class SynchronizedExample1 {
// 修飾一個代碼塊
public void test1() {
synchronized (this) {
for (int i = 0; i < 10; i++) {
log.info("test1 - {}", i);
}
}
}
// 修飾一個方法
public synchronized void test2() {
for (int i = 0; i < 10; i++) {
log.info("test - {}", i);
}
}
public static void main(String[] args) {
// 聲明一個實(shí)例
SynchronizedExample1 example1 = new SynchronizedExample1();
SynchronizedExample1 example2 = new SynchronizedExample1();
// 聲明一個線程池
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.execute(()->{
example1.test1();
});
executorService.execute(()->{
example2.test1();
});
}
}
打印結(jié)果节榜,是交替出現(xiàn)的羡玛,也不一定是交替,不同對象調(diào)用時互相不影響的
15:35:12.188 [pool-1-thread-2] INFO com.cuzz.concurrency.example.sync.SynchronizedExample1 - test1 - 0
15:35:12.188 [pool-1-thread-1] INFO com.cuzz.concurrency.example.sync.SynchronizedExample1 - test1 - 0
15:35:12.196 [pool-1-thread-1] INFO com.cuzz.concurrency.example.sync.SynchronizedExample1 - test1 - 1
15:35:12.196 [pool-1-thread-2] INFO com.cuzz.concurrency.example.sync.SynchronizedExample1 - test1 - 1
15:35:12.196 [pool-1-thread-1] INFO com.cuzz.concurrency.example.sync.SynchronizedExample1 - test1 - 2
15:35:12.196 [pool-1-thread-2] INFO com.cuzz.concurrency.example.sync.SynchronizedExample1 - test1 - 2
15:35:12.196 [pool-1-thread-1] INFO com.cuzz.concurrency.example.sync.SynchronizedExample1 - test1 - 3
15:35:12.196 [pool-1-thread-2] INFO com.cuzz.concurrency.example.sync.SynchronizedExample1 - test1 - 3
15:35:12.196 [pool-1-thread-1] INFO com.cuzz.concurrency.example.sync.SynchronizedExample1 - test1 - 4
15:35:12.196 [pool-1-thread-2] INFO com.cuzz.concurrency.example.sync.SynchronizedExample1 - test1 - 4
15:35:12.196 [pool-1-thread-1] INFO com.cuzz.concurrency.example.sync.SynchronizedExample1 - test1 - 5
15:35:12.196 [pool-1-thread-2] INFO com.cuzz.concurrency.example.sync.SynchronizedExample1 - test1 - 5
15:35:12.197 [pool-1-thread-1] INFO com.cuzz.concurrency.example.sync.SynchronizedExample1 - test1 - 6
15:35:12.197 [pool-1-thread-2] INFO com.cuzz.concurrency.example.sync.SynchronizedExample1 - test1 - 6
15:35:12.197 [pool-1-thread-1] INFO com.cuzz.concurrency.example.sync.SynchronizedExample1 - test1 - 7
15:35:12.197 [pool-1-thread-2] INFO com.cuzz.concurrency.example.sync.SynchronizedExample1 - test1 - 7
15:35:12.197 [pool-1-thread-1] INFO com.cuzz.concurrency.example.sync.SynchronizedExample1 - test1 - 8
15:35:12.197 [pool-1-thread-2] INFO com.cuzz.concurrency.example.sync.SynchronizedExample1 - test1 - 8
15:35:12.197 [pool-1-thread-1] INFO com.cuzz.concurrency.example.sync.SynchronizedExample1 - test1 - 9
15:35:12.197 [pool-1-thread-2] INFO com.cuzz.concurrency.example.sync.SynchronizedExample1 - test1 - 9
- 修飾方法:整個方法宗苍,作用于調(diào)用的對象
與上面類似 - 修飾靜態(tài)方法:整個靜態(tài)方法稼稿,作用于所有對象
public class SynchronizedExample2 {
// 修飾一個類
public static void test1(int j) {
synchronized (SynchronizedExample2.class) {
for (int i = 0; i < 10; i++) {
log.info("test1 -{} - {}",j, i);
}
}
}
// 修飾一個靜態(tài)方法
public static synchronized void test2(int j) {
for (int i = 0; i < 10; i++) {
log.info("test2 - {} - {}", i);
}
}
public static void main(String[] args) {
// 聲明一個實(shí)例
SynchronizedExample2 example1 = new SynchronizedExample2();
SynchronizedExample2 example2 = new SynchronizedExample2();
// 聲明一個線程池
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.execute(()->{
example1.test1(1);
});
executorService.execute(()->{
example2.test1(2);
});
}
}
使用不同的對象,打印出來讳窟,是先打印test1 -1 線程的让歼,再打印test1 - 2這個線程
15:46:20.320 [pool-1-thread-1] INFO com.cuzz.concurrency.example.sync.SynchronizedExample2 - test1 -1 - 0
15:46:20.330 [pool-1-thread-1] INFO com.cuzz.concurrency.example.sync.SynchronizedExample2 - test1 -1 - 1
15:46:20.330 [pool-1-thread-1] INFO com.cuzz.concurrency.example.sync.SynchronizedExample2 - test1 -1 - 2
15:46:20.330 [pool-1-thread-1] INFO com.cuzz.concurrency.example.sync.SynchronizedExample2 - test1 -1 - 3
15:46:20.330 [pool-1-thread-1] INFO com.cuzz.concurrency.example.sync.SynchronizedExample2 - test1 -1 - 4
15:46:20.330 [pool-1-thread-1] INFO com.cuzz.concurrency.example.sync.SynchronizedExample2 - test1 -1 - 5
15:46:20.330 [pool-1-thread-1] INFO com.cuzz.concurrency.example.sync.SynchronizedExample2 - test1 -1 - 6
15:46:20.330 [pool-1-thread-1] INFO com.cuzz.concurrency.example.sync.SynchronizedExample2 - test1 -1 - 7
15:46:20.330 [pool-1-thread-1] INFO com.cuzz.concurrency.example.sync.SynchronizedExample2 - test1 -1 - 8
15:46:20.330 [pool-1-thread-1] INFO com.cuzz.concurrency.example.sync.SynchronizedExample2 - test1 -1 - 9
15:46:20.330 [pool-1-thread-2] INFO com.cuzz.concurrency.example.sync.SynchronizedExample2 - test1 -2 - 0
15:46:20.330 [pool-1-thread-2] INFO com.cuzz.concurrency.example.sync.SynchronizedExample2 - test1 -2 - 1
15:46:20.330 [pool-1-thread-2] INFO com.cuzz.concurrency.example.sync.SynchronizedExample2 - test1 -2 - 2
15:46:20.330 [pool-1-thread-2] INFO com.cuzz.concurrency.example.sync.SynchronizedExample2 - test1 -2 - 3
15:46:20.330 [pool-1-thread-2] INFO com.cuzz.concurrency.example.sync.SynchronizedExample2 - test1 -2 - 4
15:46:20.330 [pool-1-thread-2] INFO com.cuzz.concurrency.example.sync.SynchronizedExample2 - test1 -2 - 5
15:46:20.330 [pool-1-thread-2] INFO com.cuzz.concurrency.example.sync.SynchronizedExample2 - test1 -2 - 6
15:46:20.330 [pool-1-thread-2] INFO com.cuzz.concurrency.example.sync.SynchronizedExample2 - test1 -2 - 7
15:46:20.331 [pool-1-thread-2] INFO com.cuzz.concurrency.example.sync.SynchronizedExample2 - test1 -2 - 8
15:46:20.331 [pool-1-thread-2] INFO com.cuzz.concurrency.example.sync.SynchronizedExample2 - test1 -2 - 9
- 修飾類:括號括起來的部分,作用于所有對象
與上面一致 - 計數(shù)
在add方法上添加一個synchronized關(guān)鍵字
@Slf4j
@ThreadSafe
public class CountExample3 {
// 請求總數(shù)
public static int clientTotal = 5000;
// 同時并發(fā)執(zhí)行的線程數(shù)
public static int threadTotal = 200;
public static int count = 0;
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newCachedThreadPool();
final Semaphore semaphore = new Semaphore(threadTotal);
final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
for (int i = 0; i < clientTotal ; i++) {
executorService.execute(() -> {
try {
semaphore.acquire();
add();
semaphore.release();
} catch (Exception e) {
log.error("exception", e);
}
countDownLatch.countDown();
});
}
countDownLatch.await();
executorService.shutdown();
log.info("count:{}", count);
}
private synchronized static void add() {
count++;
}
}
輸出結(jié)果
15:52:21.184 [main] INFO com.cuzz.concurrency.example.count.CountExample3 - count:500
-
對比
4-4 線程安全性-可見性
-
導(dǎo)致共享變量在線程間不可見的原因
-
JMM關(guān)于synchronized的兩天規(guī)定
-
volatile
-
volatile寫
-
volatile讀
- 計數(shù)
使用volatile關(guān)鍵字丽啡,也不是線程安全的
當(dāng)我count++時
分為3步 1. 從主存中讀取count 2. +1 3. 把count寫回主存
當(dāng)兩個兩個線程同時獲取時谋右,雖然它們都是讀的最新的值,但是有時候會少計數(shù)
@Slf4j
@NotThreadSafe
public class CountExample4 {
// 請求總數(shù)
public static int clientTotal = 5000;
// 同時并發(fā)執(zhí)行的線程數(shù)
public static int threadTotal = 200;
public static volatile int count = 0;
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newCachedThreadPool();
final Semaphore semaphore = new Semaphore(threadTotal);
final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
for (int i = 0; i < clientTotal ; i++) {
executorService.execute(() -> {
try {
semaphore.acquire();
add();
semaphore.release();
} catch (Exception e) {
log.error("exception", e);
}
countDownLatch.countDown();
});
}
countDownLatch.await();
executorService.shutdown();
log.info("count:{}", count);
}
private static void add() {
count++;
}
}
-
volatile使用
4-5 線程安全性-有序性
-
有序性
-
happens-before原則
4-6 線程安全性總結(jié)
第5章 安全發(fā)布對象
本章主要講解安全發(fā)布對象的一些核心方法补箍,主要通過單例類的多種實(shí)現(xiàn)方式改执,讓大家在實(shí)現(xiàn)過程中去體會這些方法的具體含義。這一章也是對線程安全性的鞏固坑雅,也是把線程安全性涉及的一些關(guān)鍵字和類再一次放到實(shí)際場景中使用辈挂,加深大家對他們的印象和認(rèn)識。
5-1 安全發(fā)布對象-發(fā)布與逸出
- 不安全
這是一個不安全的類裹粤,當(dāng)運(yùn)行時终蒂,任何線程都可修改這個類的屬性,重合改變狀態(tài)
@Slf4j
public class UnsafePublic {
private String[] states = {"a", "b", "c"};
public String[] getStrates() {
return states;
}
public static void main(String[] args) {
UnsafePublic unsafePublic = new UnsafePublic();
log.info("{}", Arrays.toString(unsafePublic.getStrates()));
unsafePublic.getStrates()[0] = "d";
log.info("{}", Arrays.toString(unsafePublic.getStrates()));
}
}
- 逸出
對象沒有被正常構(gòu)造完全之前遥诉,它就會被發(fā)布拇泣,在對象還沒完成之前,不能將其發(fā)布矮锈。
@Slf4j
public class Escape {
private int thisCanBeEscape = 0;
public Escape() {
new InnerClass();
}
private class InnerClass{
public InnerClass() {
log.info("{}", Escape.this.thisCanBeEscape);
}
}
public static void main(String[] args) {
new Escape();
}
}
5-2 安全發(fā)布對象-四種方法
如果一個對象時可變對象霉翔,就要安全的發(fā)布。
- 在靜態(tài)初始化函數(shù)中初始化一個對象的引用
- 懶漢模式
在單線程運(yùn)行下沒有問題愕难,但是在多線程下會出現(xiàn)問題,當(dāng)兩個線程都執(zhí)行到if (instance == null)
時候都會去創(chuàng)建一個新的對象。
/**
* 懶漢模式
* 單例實(shí)例在第一次使用的時候創(chuàng)建出來
*/
@NotThreadSafe
public class SingletonExample1 {
// 私有的構(gòu)造函數(shù)
private SingletonExample1() {
}
// 單例對象
private static SingletonExample1 instance = null;
// 靜態(tài)的工廠方法
public static SingletonExample1 getInstance() {
if (instance == null) {
instance = new SingletonExample1();
}
return instance;
}
}
- 餓漢模式
可能會使類加載非常慢猫缭,可能引起性能問題葱弟,如果只加載而不調(diào)用,會產(chǎn)生資源的浪費(fèi)
使用餓漢模式猜丹,要保證2點(diǎn)芝加,第一是類加載比較簡單,第二是這個類肯定會被使用
/**
* 餓漢模式
* 單例實(shí)例在類裝載使用時候創(chuàng)建出來
*/
@ThreadSafe
public class SingletonExample2 {
// 私有的構(gòu)造函數(shù)
private SingletonExample2() {
}
// 單例對象
private static SingletonExample2 instance = new SingletonExample2();
// 靜態(tài)的工廠方法
public static SingletonExample2 getInstance() {
return instance;
}
}
- 懶漢模式(線程安全的-不推薦)
帶來性能的開銷
/**
* 懶漢模式
* 單例實(shí)例在第一次使用的時候創(chuàng)建出來
*/
@ThreadSafe
@NotRecommend
public class SingletonExample3 {
// 私有的構(gòu)造函數(shù)
private SingletonExample3() {
}
// 單例對象
private static SingletonExample3 instance = null;
// 靜態(tài)的工廠方法
public static synchronized SingletonExample3 getInstance() {
if (instance == null) {
instance = new SingletonExample3();
}
return instance;
}
}
-
懶漢模式(不是線程安全的-雙重同步鎖)
// 1. memory = allocate() 分配對象空間
// 2. ctorInstance() 初始化對象
// 3. instance = memory 設(shè)置instance指向剛分配的內(nèi)存// JVM和cpu優(yōu)化射窒,發(fā)生了指令重排
// 1. memory = allocate() 分配對象空間
// 3. instance = memory 設(shè)置instance指向剛分配的內(nèi)存
// 2. ctorInstance() 初始化對象
指令重排藏杖,如果線程B在判斷時候已經(jīng)發(fā)現(xiàn)分配空間,就返回了instance脉顿,其實(shí)A還沒有初始化對象蝌麸。
/**
* 懶漢模式 雙重鎖同步單例模式
* 單例實(shí)例在第一次使用的時候創(chuàng)建出來
*/
@NotThreadSafe
public class SingletonExample4 {
// 私有的構(gòu)造函數(shù)
private SingletonExample4() {
}
// 單例對象
private static SingletonExample4 instance = null;
// 靜態(tài)的工廠方法
public static SingletonExample4 getInstance() {
if (instance == null) { // 雙重檢測機(jī)制 // B
synchronized (SingletonExample4.class) { // 同步鎖 // A - 3
if (instance == null) {
instance = new SingletonExample4();
}
}
}
return instance;
}
}
- 懶漢模式(線程安全的-雙重同步鎖-volatile)
volatile禁止指令重排
/**
* 懶漢模式 雙重鎖同步單例模式
* 單例實(shí)例在第一次使用的時候創(chuàng)建出來
*/
@ThreadSafe
public class SingletonExample5 {
// 私有的構(gòu)造函數(shù)
private SingletonExample5() {
}
// 1. memory = allocate() 分配對象空間
// 2. ctorInstance() 初始化對象
// 3. instance = memory 設(shè)置instance指向剛分配的內(nèi)存
// JVM和cpu優(yōu)化,發(fā)生了指令重排
// 1. memory = allocate() 分配對象空間
// 3. instance = memory 設(shè)置instance指向剛分配的內(nèi)存
// 2. ctorInstance() 初始化對象
// 單例對象 // 加上volatile防止指令重排
private volatile static SingletonExample5 instance = null;
// 靜態(tài)的工廠方法
public static SingletonExample5 getInstance() {
if (instance == null) { // 雙重檢測機(jī)制
synchronized (SingletonExample5.class) { // 同步鎖
if (instance == null) {
instance = new SingletonExample5();
}
}
}
return instance;
}
}
- 餓漢模式(注意)
靜態(tài)域一定要再靜態(tài)代碼塊之前艾疟,因?yàn)樗麄兪前凑昭驁?zhí)行的来吩,否者會報空指針異常
/**
* 餓漢模式
* 單例實(shí)例在類裝載使用時候創(chuàng)建出來
*/
@ThreadSafe
public class SingletonExample6 {
// 私有的構(gòu)造函數(shù)
private SingletonExample6() {
}
// 單例對象
// 靜態(tài)域一定要再靜態(tài)代碼塊之前,因?yàn)樗麄兪前凑昭驁?zhí)行的
private static SingletonExample6 instance = null;
static {
instance = new SingletonExample6();
}
// 靜態(tài)的工廠方法
public static SingletonExample6 getInstance() {
return instance;
}
}
- 枚舉(線程安全的-推薦)
/**
* 枚舉模式:最安全的
*/
@ThreadSafe
@Recommend
public class SingletonExample7 {
// 私有構(gòu)造函數(shù)
private SingletonExample7() {
}
public static SingletonExample7 getInstance() {
return Singleton.INSTANCE.getInstance();
}
private enum Singleton {
INSTANCE;
private SingletonExample7 singleton;
// JVM保證這個方法絕對只調(diào)用一次
Singleton() {
singleton = new SingletonExample7();
}
public SingletonExample7 getInstance() {
return singleton;
}
}
}
第6章 線程安全策略
本章主要講解線程安全策略蔽莱,包括定義不可變對象弟疆、線程封閉、同步容器盗冷、并發(fā)容器等怠苔,引出并發(fā)里的關(guān)鍵知識J.U.C。同時還額外介紹了開發(fā)中常見的一些線程不安全類和寫法仪糖,并給出他們各自對應(yīng)的替代方案柑司。這一章涉及的內(nèi)容在日常開發(fā)和面試中都會涉及很多。
6-1 不可變對象-1
-
不可變對象需要的滿足條件
-
final關(guān)鍵字
public class ImmutableExample1 {
private final static Integer a = 1;
private final static String b = "2";
private final static Map<Integer, Integer> map = new HashMap<>();
static {
map.put(1, 2);
map.put(3, 4);
map.put(5, 6);
}
// 可以保證變量不發(fā)生變化
private void test(final int a) {
// a = 1; // 編譯出錯
}
public static void main(String[] args) {
// a = 2; // 編譯出錯
// b = "3"; // 編譯出錯
// map = new HashMap<>(); // 編譯出錯
map.put(1, 3); // 引用對象可以修改里面的值
}
}
6-2 不可變對象-2
@ThreadSafe
public class ImmutableExample2 {
private static Map<Integer, Integer> map = new HashMap<>();
static {
map.put(1, 2);
map.put(3, 4);
map.put(5, 6);
map = Collections.unmodifiableMap(map);
}
public static void main(String[] args) {
map.put(1, 3); // 運(yùn)行時報錯
}
}
6-3 線程封閉-1
還不是很清楚乓诽,可以看看以下博客
ThreadLocal
6-4 線程封閉-2
6-5 線程不安全類與寫法-1
- 先檢查再執(zhí)行:if(condition(a)){handle(a)}帜羊,這樣的很容易引起線程不安全
- StringBuilder -> StringBuffer
StringBuffer加了synchronized關(guān)鍵字同步,影響性能 - SimpleDataFormat -> JodaTime
- ArrayList, HashSet, HashMap等Collections
6-6 線程不安全類與寫法-2
6-7 同步容器-1
- ArrayList -> Vector, Stack
- HashMap -> HashTable(key value 不能為null)
- Collections.synchronizedXXX(List Set Map)
6-8 同步容器-2
在使用foreach和iterator的時候不要刪除元素鸠天。
同步容器使用synchronized性能不好讼育,同步容器也不是完全線程安全的。
6-9 并發(fā)容器及安全共享策略總結(jié)
- ArrayList -> CopyOnWriteArrayList
寫操作是需要拷貝數(shù)組稠集,消耗內(nèi)存奶段,不能實(shí)時一致性 - HashSet TreeSet -> CopyOnWriteArraySet ConcurrentSkipListSet
-
HashMap TreeMap -> ConcurrentHashMap ConcurrentSkioListMap
第7章 J.U.C之AQS
AQS(AbstractQueuedSynchronizer)是J.U.C的重要組件,也是面試的重要考點(diǎn)剥纷。這一章里將重點(diǎn)講解AQS模型設(shè)計及相關(guān)同步組件的原理和使用痹籍,都非常實(shí)用,具體包括:CountDownLatch晦鞋、Semaphore蹲缠、CyclicBarrier棺克、ReentrantLock與鎖、Condition等线定。這些組件需要大家能熟練明白他們的用途及差異娜谊,不但會使用,而且還要明確知道不同方法調(diào)用后的不同效果斤讥。
7-1 J.U.C之AQS-介紹
大致思路:首先AQS內(nèi)部維護(hù)了一個CHL隊(duì)列來管理鎖纱皆,線程會首選嘗試獲取鎖,如果獲取失敗芭商,就把當(dāng)前線程信息包裝成一個節(jié)點(diǎn)鎖派草,加入到隊(duì)列中。然后循環(huán)嘗試獲取鎖铛楣,當(dāng)持有鎖釋放鎖時近迁,會喚醒后繼鎖。
7-2 J.U.C之AQS-CountDownLatch
- 程序執(zhí)行需要等待某個條件后才執(zhí)行
所有線程完成之后才會輸出finished
public class CountDownLatchExample1 {
private final static int threadCount = 200;
public static void main(String[] args) throws InterruptedException {
ExecutorService exec = Executors.newCachedThreadPool();
final CountDownLatch countDownLatch = new CountDownLatch(threadCount);
for (int i = 0; i < threadCount; i++) {
final int threadNum = i;
exec.execute(() ->{
try {
test(threadNum);
} catch (Exception e) {
log.error("exception", e);
} finally {
countDownLatch.countDown();
}
});
}
countDownLatch.await();
log.info("finished.");
exec.shutdown();
}
private static void test(int threadNum) throws InterruptedException {
Thread.sleep(100);
log.info("{}", threadNum);
}
}
其中的countDownLathc.await(), 還可以接受2個參數(shù):時間和單位蛉艾。其含義是超過這個時間就不管了钳踊。繼續(xù)執(zhí)行下面的代碼。
7-3 J.U.C之AQS-Semaphore
計數(shù)信號量
有限訪問的資源
對并發(fā)訪問的控制
public class SemaphoreExample1 {
private final static int threadCount = 200;
public static void main(String[] args) throws InterruptedException {
ExecutorService exec = Executors.newCachedThreadPool();
// 并發(fā)量為20
final Semaphore semaphore = new Semaphore(20);
for (int i = 0; i < threadCount; i++) {
final int threadNum = i;
exec.execute(() ->{
try {
semaphore.acquire(); // 獲取一個許可
test(threadNum);
semaphore.release(); // 釋放許可
} catch (Exception e) {
log.error("exception", e);
}
});
}
exec.shutdown();
}
private static void test(int threadNum) throws InterruptedException {
log.info("{}", threadNum);
Thread.sleep(1000);
}
}
- tryAcquire()
public class SemaphoreExample2 {
private final static int threadCount = 200;
public static void main(String[] args) throws InterruptedException {
ExecutorService exec = Executors.newCachedThreadPool();
// 并發(fā)量為20
final Semaphore semaphore = new Semaphore(20);
for (int i = 0; i < threadCount; i++) {
final int threadNum = i;
exec.execute(() ->{
try {
if (semaphore.tryAcquire()) { // 嘗試獲取一個許可
test(threadNum);
semaphore.release(); // 釋放許可
}
} catch (Exception e) {
log.error("exception", e);
}
});
}
exec.shutdown();
}
private static void test(int threadNum) throws InterruptedException {
log.info("{}", threadNum);
Thread.sleep(1000);
}
}
7-4 J.U.C之AQS-CyclicBarrier
告訴我們有多少個值同步等待勿侯。
public class CyclicBarrierExample1 {
private static CyclicBarrier barrier = new CyclicBarrier(5);
public static void main(String[] args) throws InterruptedException {
ExecutorService executor= Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
final int threadNum = i;
Thread.sleep(1000);
executor.execute(()->{
try {
race(threadNum);
} catch (Exception e) {
log.error("exception", e);
}
});
}
}
private static void race(int threadNum) throws Exception{
Thread.sleep(1000);
log.info("{} is ready", threadNum);
barrier.await();
log.info("{} continue", threadNum);
}
}
7-5 J.U.C之AQS-ReentrantLock與鎖-1
public class LockExample1 {
// 請求總數(shù)
public static int clientTotal = 5000;
// 同時并發(fā)執(zhí)行的線程數(shù)
public static int threadTotal = 200;
public static int count = 0;
private final static Lock lock = new ReentrantLock();
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newCachedThreadPool();
final Semaphore semaphore = new Semaphore(threadTotal);
final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
for (int i = 0; i < clientTotal ; i++) {
executorService.execute(() -> {
try {
semaphore.acquire();
add();
semaphore.release();
} catch (Exception e) {
log.error("exception", e);
}
countDownLatch.countDown();
});
}
countDownLatch.await();
executorService.shutdown();
log.info("count:{}", count);
}
private static void add() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
}
- Condition
public class LockExample4 {
public static void main(String[] args) {
ReentrantLock reentrantLock = new ReentrantLock();
Condition condition = reentrantLock.newCondition();
new Thread(() -> {
try {
reentrantLock.lock();
log.info("wait signal"); // 1
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("get signal"); // 4
reentrantLock.unlock();
}).start();
new Thread(() -> {
reentrantLock.lock();
log.info("get lock"); // 2
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
condition.signalAll();
log.info("send signal ~ "); // 3
reentrantLock.unlock();
}).start();
}
}
7-6 J.U.C之AQS-ReentrantLock與鎖-2
- ReentrantReadWriteLock 悲觀讀寫鎖
public class LockExample2 {
private final Map<String, Data> map = new TreeMap<>();
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
private final Lock readLock = lock.readLock();
private final Lock writeLock = lock.writeLock();
public Data get(String key) {
readLock.lock();
try {
return map.get(key);
} finally {
readLock.unlock();
}
}
public Set<String> getAllKeys() {
readLock.lock();
try {
return map.keySet();
} finally {
readLock.unlock();
}
}
public Data put(String key, Data value) {
writeLock.lock();
try {
return map.put(key, value);
} finally {
readLock.unlock();
}
}
class Data {
}
}
- StampleLock
樂觀鎖
public class LockExample3 {
class Point {
private double x, y;
private final StampedLock sl = new StampedLock();
void move(double deltaX, double deltaY) { // an exclusively locked method
long stamp = sl.writeLock();
try {
x += deltaX;
y += deltaY;
} finally {
sl.unlockWrite(stamp);
}
}
//下面看看樂觀讀鎖案例
double distanceFromOrigin() { // A read-only method
long stamp = sl.tryOptimisticRead(); //獲得一個樂觀讀鎖
double currentX = x, currentY = y; //將兩個字段讀入本地局部變量
if (!sl.validate(stamp)) { //檢查發(fā)出樂觀讀鎖后同時是否有其他寫鎖發(fā)生拓瞪?
stamp = sl.readLock(); //如果沒有,我們再次獲得一個讀悲觀鎖
try {
currentX = x; // 將兩個字段讀入本地局部變量
currentY = y; // 將兩個字段讀入本地局部變量
} finally {
sl.unlockRead(stamp);
}
}
return Math.sqrt(currentX * currentX + currentY * currentY);
}
//下面是悲觀讀鎖案例
void moveIfAtOrigin(double newX, double newY) { // upgrade
// Could instead start with optimistic, not read mode
long stamp = sl.readLock();
try {
while (x == 0.0 && y == 0.0) { //循環(huán)助琐,檢查當(dāng)前狀態(tài)是否符合
long ws = sl.tryConvertToWriteLock(stamp); //將讀鎖轉(zhuǎn)為寫鎖
if (ws != 0L) { //這是確認(rèn)轉(zhuǎn)為寫鎖是否成功
stamp = ws; //如果成功 替換票據(jù)
x = newX; //進(jìn)行狀態(tài)改變
y = newY; //進(jìn)行狀態(tài)改變
break;
} else { //如果不能成功轉(zhuǎn)換為寫鎖
sl.unlockRead(stamp); //我們顯式釋放讀鎖
stamp = sl.writeLock(); //顯式直接進(jìn)行寫鎖 然后再通過循環(huán)再試
}
}
} finally {
sl.unlock(stamp); //釋放讀鎖或?qū)戞i
}
}
}
}
第8章 J.U.C組件拓展
這一章繼續(xù)講解J.U.C相關(guān)組件祭埂,主要包括FutureTask、Fork/Join框架兵钮、BlockingQueue蛆橡,其中FutureTask講解時會對比著Callable、Runnable掘譬、Future來講泰演。這些組件使用場景相對AQS會少一些,但也是J.U.C的重要組成部分葱轩,也是需要掌握的睦焕。
8-1 J.U.C-FutureTask-1
- Future
@Slf4j
public class FutureExample {
static class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
log.info("do something in callable");
sleep(5000);
return "Done";
}
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<String> future = executor.submit(new MyCallable());
log.info("do something in main");
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
String res = future.get();
log.info("res: {}", res);
}
}
8-2 J.U.C-FutureTask-2
- FutureTask
@Slf4j
public class FutureTaskExample {
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<String> futureTask = new FutureTask<String>(new Callable<String>() {
@Override
public String call() throws Exception {
log.info("do something in callable");
sleep(5000);
return "Done";
}
});
new Thread(futureTask).start();
log.info("do something in main");
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
String res = futureTask.get();
log.info("res: {}", res);
}
}
8-3 J.U.C-ForkJoin
用于并行計算
@Slf4j
public class ForkJoinTaskExample extends RecursiveTask<Integer> {
public static final int threshold = 2;
private int start;
private int end;
public ForkJoinTaskExample(int start, int end) {
this.start = start;
this.end = end;
}
@Override
protected Integer compute() {
int sum = 0;
//如果任務(wù)足夠小就計算任務(wù)
boolean canCompute = (end - start) <= threshold;
if (canCompute) {
for (int i = start; i <= end; i++) {
sum += i;
}
} else {
// 如果任務(wù)大于閾值,就分裂成兩個子任務(wù)計算
int middle = (start + end) / 2;
ForkJoinTaskExample leftTask = new ForkJoinTaskExample(start, middle);
ForkJoinTaskExample rightTask = new ForkJoinTaskExample(middle + 1, end);
// 執(zhí)行子任務(wù)
leftTask.fork();
rightTask.fork();
// 等待任務(wù)執(zhí)行結(jié)束合并其結(jié)果
int leftResult = leftTask.join();
int rightResult = rightTask.join();
// 合并子任務(wù)
sum = leftResult + rightResult;
}
return sum;
}
public static void main(String[] args) {
ForkJoinPool forkjoinPool = new ForkJoinPool();
//生成一個計算任務(wù)靴拱,計算1+2+3+4
ForkJoinTaskExample task = new ForkJoinTaskExample(1, 100);
//執(zhí)行一個任務(wù)
Future<Integer> result = forkjoinPool.submit(task);
try {
log.info("result:{}", result.get());
} catch (Exception e) {
log.error("exception", e);
}
}
}
8-4 J.U.C-BlockingQueue
第9章 線程調(diào)度-線程池
本章講解J.U.C里最后一部分:線程池垃喊。面試大概率會問到線程池相關(guān)的知識點(diǎn)。這一章將主要從new Thread弊端袜炕、線程池的好處本谜、ThreadPoolExecutor詳細(xì)介紹(參數(shù)、狀態(tài)偎窘、方法)乌助、線程池類圖溜在、Executor框架接口等進(jìn)行講解,需要大家能了解線程池的許多細(xì)節(jié)及配置他托,并能在實(shí)際項(xiàng)目中正確使用炕泳。...
9-1 線程池-1
-
Thread的弊端
-
線程池的好處
-
ThreadPoolExecutor
9-2 線程池-2
-
執(zhí)行狀態(tài)
-
方法
-
線程池類圖
-
Executor框架接口
newCachedThreadPool
@Slf4j
public class ThreadPoolExample1 {
public static void main(String[] args) {
ExecutorService executor = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
final int index = i;
executor.execute(new Runnable() {
@Override
public void run() {
log.info("task:{}", index);
}
});
}
executor.shutdown();
}
}
- newFixedThreadPool
@Slf4j
public class ThreadPoolExample2 {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(3);
for (int i = 0; i < 10; i++) {
final int index = i;
executor.execute(new Runnable() {
@Override
public void run() {
log.info("task:{}", index);
}
});
}
executor.shutdown();
}
}
- newScheduledThreadPool
@Slf4j
public class ThreadPoolExample3 {
public static void main(String[] args) {
ScheduledExecutorService executor = Executors.newScheduledThreadPool(3);
// executor.schedule(new Runnable() {
// @Override
// public void run() {
// log.warn("schedule run");
// }
// }, 3, TimeUnit.SECONDS);
// 啟動是延遲1秒然后每隔3秒執(zhí)行任務(wù)
executor.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
log.warn("schedule run");
}
}, 1, 3, TimeUnit.SECONDS);
//executor.shutdown();
}
}
9-3 線程池-3
-
線程池 - 合理配置
第10章 多線程并發(fā)拓展
本章會對并發(fā)編程做些補(bǔ)充,但都貼近當(dāng)前的面試上祈,主要講解死鎖產(chǎn)生的條件及預(yù)防、多線程并發(fā)編程的最佳實(shí)踐浙芙、Spring與線程安全登刺、以及面試都特別喜歡問的HashMap和ConcurrentMap源碼細(xì)節(jié)。當(dāng)然嗡呼,面試喜歡問的問題纸俭,對實(shí)際項(xiàng)目開發(fā)也是特別重要的。
10-1 死鎖
-
死鎖的必要條件
@Slf4j
public class DeadLock implements Runnable{
public int flag = 1;
private String str1;
private String str2;
public DeadLock(String str1, String str2) {
this.str1 = str1;
this.str2 = str2;
}
@Override
public void run() {
if (flag == 1) {
synchronized (str1) {
try {
Thread.sleep(500);
} catch (Exception e) {
e.printStackTrace();
}
synchronized (str2) {
log.info("1");
}
}
}
if (flag == 0) {
synchronized (str2) {
try {
Thread.sleep(500);
} catch (Exception e) {
e.printStackTrace();
}
synchronized (str1) {
log.info("0");
}
}
}
}
public static void main(String[] args) {
String str1 = new String("a");
String str2 = new String("b");
DeadLock td1 = new DeadLock(str1, str2);
DeadLock td2 = new DeadLock(str1, str2);
td1.flag = 1;
td2.flag = 0;
new Thread(td1).start();
new Thread(td2).start();
}
}
10-2 并發(fā)最佳實(shí)踐
10-3 Spring與線程安全
10-4 HashMap與ConcurrentHashMap解析
-
HashMap
在多線程的環(huán)境下南窗,可能出現(xiàn)循環(huán)鏈表
-
ConcurrentHashMap
java7使用的分布鎖
java8
10-5 多線程并發(fā)與線程安全總結(jié)
第11章 高并發(fā)之?dāng)U容思路
本章重點(diǎn)是讓大家學(xué)會解決高并發(fā)問題的思路和手段揍很,及重點(diǎn)類的使用。在擴(kuò)容講解時万伤,首先通過例子介紹垂直擴(kuò)容和水平擴(kuò)容的區(qū)別窒悔,之后詳細(xì)介紹數(shù)據(jù)庫的讀操作擴(kuò)展和寫操作擴(kuò)展。擴(kuò)容這個最基本的手段敌买,相信大家都不會有什么問題,關(guān)鍵是根據(jù)實(shí)際場景分析做什么樣的擴(kuò)容。
11-1 高并發(fā)之?dāng)U容思路
-
擴(kuò)容
-
擴(kuò)容 - 數(shù)據(jù)庫
第12章 高并發(fā)之緩存思路
本章講解高并發(fā)中緩存方案挺尿。包含對緩存特征(命中率而姐、最大元素、清空策略)芙粱、影響緩存命中率因素祭玉、緩存分類和應(yīng)用場景(本地緩存、分布式緩存)春畔、高并發(fā)場景下緩存常見問題(緩存一致性脱货、緩存并發(fā)、緩存穿透拐迁、雪崩)等的具體介紹蹭劈。此外,針對大家常用的緩存組件Guava Cache线召、Memcache铺韧、Redis也做了原理性的分析,并且演示缓淹。
12-1 高并發(fā)之緩存-特征哈打、場景及組件介紹-1
-
緩存特征
-
緩存命中率影響因數(shù)
-
緩存分類和應(yīng)用場景
12-2 高并發(fā)之緩存-特征塔逃、場景及組件介紹-2
-
Guava Cache
-
Memcache
-
Redis
12-3 高并發(fā)之緩存-redis的使用
12-4 高并發(fā)之緩存-高并發(fā)場景問題及實(shí)戰(zhàn)講解
-
緩存一致性
-
緩存并發(fā)問題
-
緩存穿透問題
-
緩存的雪崩現(xiàn)象
第13章 高并發(fā)之消息隊(duì)列思路
本章重點(diǎn)介紹了消息隊(duì)列的特性(業(yè)務(wù)無關(guān)、FIFO料仗、容災(zāi)湾盗、性能)、為什么需要消息隊(duì)列以及消息隊(duì)列的好處(業(yè)務(wù)解耦立轧、最終一致性格粪、廣播、錯峰與流控)氛改,并在最后對當(dāng)前比較流行的消息隊(duì)列組件kafka和rabbitmq做了架構(gòu)分析和特性介紹帐萎,讓大家對消息隊(duì)列能有明確的認(rèn)識。
13-1 高并發(fā)之消息隊(duì)列-1
13-2 高并發(fā)之消息隊(duì)列-2
-
消息隊(duì)列特性
-
為什么需要消息隊(duì)列
-
消息隊(duì)列好處
13-3 高并發(fā)之消息隊(duì)列-3
-
Kafka
-
RabbitMQ
第14章 高并發(fā)之應(yīng)用拆分思路
本章直接從實(shí)際項(xiàng)目拆分步驟講起胜卤,讓大家可以實(shí)際感受到應(yīng)用拆分的好處和解決的問題疆导,之后引出對應(yīng)用拆分原則(業(yè)務(wù)優(yōu)先、循序漸進(jìn)葛躏、兼顧技術(shù)澈段、可靠測試)和應(yīng)用拆分時思考的內(nèi)容(應(yīng)用之間通信、應(yīng)用之間數(shù)據(jù)庫設(shè)計舰攒、避免事務(wù)跨應(yīng)用)败富,并引出對服務(wù)化Dubbo和微服務(wù)Spring Cloud的框架介紹。
14-1 高并發(fā)之應(yīng)用拆分-1
14-2 高并發(fā)之應(yīng)用拆分-2
-
拆分原則
-
Dubbo
-
微服務(wù)
第15章 高并發(fā)之應(yīng)用限流思路
本章從實(shí)際項(xiàng)目保存百萬數(shù)據(jù)的限流場景開始講起摩窃,讓大家感受一下某些高并發(fā)場景下使用限流和不使用限流的區(qū)別囤耳,明確限流的重要作用。之后詳細(xì)介紹了限流常用的四種算法:計數(shù)法偶芍、滑動窗口充择、漏桶算法和令牌桶算法,并對他們做了簡單的對比匪蟀。
15-1 高并發(fā)之應(yīng)用限流-1
-
計數(shù)器法
-
滑動窗口
-
漏桶算法
-
令牌桶算法
15-2 高并發(fā)之應(yīng)用限流-2
第16章 高并發(fā)之服務(wù)降級與服務(wù)熔斷思路
本章首先通過舉例讓大家明白什么是服務(wù)降級和服務(wù)熔斷椎麦,之后介紹了服務(wù)降級的分類:自動降級(超時、失敗次數(shù)材彪、故障观挎、限流)和人工降級(開關(guān)),總結(jié)了服務(wù)降級和服務(wù)熔斷的共性(目的段化、最終表現(xiàn)嘁捷、粒度、自治)和區(qū)別(出發(fā)原因显熏、管理目標(biāo)層次雄嚣、實(shí)現(xiàn)方式)以及服務(wù)降級要考慮的問題。最后介紹了Hystrix在服務(wù)降級和服務(wù)熔。
16-1 高并發(fā)之服務(wù)降級與服務(wù)熔斷思路
第17章 高并發(fā)之?dāng)?shù)據(jù)庫切庫分庫分表思路
本章從數(shù)據(jù)庫瓶頸開始講起缓升,引出對數(shù)據(jù)庫切庫分庫分表的介紹鼓鲁。數(shù)據(jù)庫切庫里重點(diǎn)介紹了讀寫分離的設(shè)計,對比支持多數(shù)據(jù)源和分庫的區(qū)別港谊;最后介紹了什么時候該考慮分表骇吭、橫向分表與縱向分表,以及通過mybatis的分頁插件shardbatis2.0實(shí)現(xiàn)數(shù)據(jù)庫分表歧寺。
17-1 高并發(fā)之?dāng)?shù)據(jù)庫切庫分庫分表
-
數(shù)據(jù)庫的瓶頸
-
數(shù)據(jù)庫切庫
-
數(shù)據(jù)庫分表
第18章 高并發(fā)之高可用手段介紹
本章主要介紹了高可用的三個常用手段:任務(wù)調(diào)度系統(tǒng)分布式燥狰、主備切換設(shè)計和引入監(jiān)控報警機(jī)制。任務(wù)調(diào)度系統(tǒng)分布式部分對 elastic-job 的優(yōu)點(diǎn)斜筐、思路碾局、特性等做了介紹,主備切換設(shè)計部分則是對zookeeper的分布式鎖這個典型應(yīng)用進(jìn)行介紹奴艾。
18-1 高并發(fā)之高可用一些手段
第19章 課程總結(jié)
本章首先對本課程的知識進(jìn)行總結(jié)回顧,然后針對面試中的并發(fā)問題與高并發(fā)問題進(jìn)行提問内斯,希望大家都能有所收獲蕴潦,并期待與大家共同探討并發(fā)與高并發(fā)的話題。