本期內容包括
JUC多線程並發、JVM和GC等目前大廠筆試中會考、面試中會問、工作中會用的高頻難點知識。
斬offer、拿高薪、跳槽神器,對標阿里P6的《尚硅谷_互聯網大廠高頻重點面試題(第2季)》發布。本套課程總結分析了2019年大廠互聯網公司常見常考的技術點,通過對40多個題目共計120集視頻詳細全面的講解,讓大家深刻掌握、扎實吃透當前的主流Java高級技術。





NoSQL數據庫Redis

消息中間件MQ




JUC多線程及高並發
並發和並行有什么區別
並發:多個線程去訪問同一個資源
並行:各種事情同時去做,一邊干什么,一邊干什么



請談談你對volatile的理解
volatile是什么

三大特性
JMM內存模型之可見性
JMM:java內存模型


各個線程對主內存中的數據進行改變,不是直接修改,而是會把age=25拷貝到自己的工作內存中再進行改變
t1改為37后要把新數據寫回到主內存中,t2,t3不知道主內存中的值已經改變了
所以我們需要有一個機制:JMM內存模型的可見性,只要有一個線程改變數據后要寫回到主內存中,其它的線程馬上就會知道主內存中的數據已經改變了
可見性的代碼驗證說明




3秒之后,a線程已經把number改了,但是main線程不知道,對main線程不可見,還在傻傻的等着,沒有人通知我
現在修改程序,加了volatile
增加了內存之間的可見性

volatile不保證原子性
原子性:不可分割、完整性,也就是某個線程正在做某個具體業務時,中間不可以被加塞或者被分割,需要整體完整,要么同時成功,要么同時失敗。
把驗證可見性的代碼寫到一個方法seeokbyvolatile
20個線程去調用addplus方法,每個線程調用1000次,則結果應該是2000。
多線程編程的模板forthread10是自己設置的快捷鍵



volatile不保證原子性理論解釋



輕量級就是乞丐版的sunchronized
不要殺雞焉用牛刀,太重了
拷貝回自己的內存空間,每個人都拿到0,寫回到主內存時,線程1寫回到的時候被掛起了,線程2歘的寫回了。然后線程1恢復后又寫回了一遍,把原來的1給覆蓋了。



三個線程都拿到1,都在各自的工作內存中加1,寫回到的時候,沒有拿到最新的值就又寫了,寫覆蓋
volatile不保證原子性問題解決






volatile指令重排案例1


不可以,必須考慮指令之間的數據依賴性
加了volatile之后是禁止指令重排
volatile指令重排案例2

答案是6
但是指令重排后,答案可能是5
所以在這些變量前面加上volitaile就可以禁止指令重排



單例模式在多線程環境下可能存在安全問題

單例模式有六種
掌握一種即可
第一步 定義私有的實例變量
第二步 構造方法
第三步 新建,返回同一個變量
比較引用類型,內存地址

在多線程壞境下,單例模式出現了問題
如果加上synchronized,在多線程的環境控制住了
但是太重了,並發性下降了
單例模式volatile分析
DCL模式 Double Check Lock雙端檢鎖機制
在加鎖之前和之后都進行一次檢測



地址不為空,但是內容為空
所以要在instance變量上面加上volatile
總結:
CAS你知道嗎?

CAS是什么
CAS compare and set 比較並交換
多個線程去操作主內存中的數據。
一個叫做期望值、一個叫做更新值




主內存中數據是5
一個線程拷貝回去自己的工作內存,對它進行修改,然后寫回到主內存的時候,會進行比較和交換,如果和拷貝的數據一樣的話,就將改變后的數據寫回去;否則的話,就不進行寫回。
CAS底層原理上

當前對象 內存偏移量









AtomicInteger 的getandincrement方法底層其實是CAS思想+套的是unsafe類的CPU原語來保證原子性,
底層思想是比較並交換,真實值和期望值相等就交換成功,否則就失敗,失敗就再來,直到比較成功為止。
CAS底層原理下






CAS缺點
CAS能夠不加鎖保證一致性,並發性加強了


原子類AtomicInteger的ABA問題談談?原子更新引用知道嗎?

ABA問題


2號線程比1號線程快,把原來的A改為B,又改為A。
1號線程回來后,期望的和原來的一樣,以為沒有改變過,於是寫回主內存。
但是中間有貓膩,2號線程已經把它改過了又改回去了。

AtomicReference原子引用

只管開頭和結尾,沒有管中間。
原子引用的泛型類
原子User類型


AtomicStampedReference版本號原子引用
時間戳原子引用

ABA問題的解決


修改成功,





我們知道ArrayList是線程不安全的,請編碼寫一個不安全的案例並給出解決方案。
集合類不安全之並發修改異常
ArrayList底層是一個數組
什么類型的數組?


數組初始長度為10,超過10以后進行擴容。
它是線程不安全的。
舉一個線程不安全的案例。


把3改為30
java.util.ConcurrentModificationException
並發修改異常
線程不安全是指:在多線程的情況下,
方法一:
Vector類可以解決這個問題,加鎖一致性可以保證,但是並發性急劇下降。
不許用Vector
方法二:
Map和Set也是不安全的集合類
方法三:
集合類不安全之寫時復制


寫時復制




鎖、擴容、寫新元素

集合類不安全之Set





集合類不安全之Map


TransferValue醒腦小練習


傳值還是傳引用

age屬於main方法的,然后調用方法時復印了一份傳給它,然后方法把復印件給改動了
我只是給你復印了一份值,原件根本沒動,所以第一個age還是20
person是main的,傳引用傳內存地址給方法,兩個引用指向了同一個地址,這時把這個地址的值改動了
str是屬於main方法的,這個池子里有了abc
這個池子里面沒有xxx,那么就重新創建一個指向它

公平鎖/非公平鎖/可重入鎖/遞歸鎖/自旋鎖 談談你的理解?請手寫一個自旋鎖。

Java鎖之公平鎖/非公平鎖





無參數:不公平 允許某個同學突然出來加塞
有參數:公平 隊列先來后到


synchronized等同於鎖,非公平鎖
被它搶到了鎖上了
Java鎖之可重入鎖和遞歸鎖理論




Java鎖之可重入鎖和遞歸鎖代碼驗證

資源類
線程操作資源類

t1線程在外層方法獲取鎖的時候,t1在進入內層方法會自動獲取鎖



只要鎖匹配,幾把鎖都可以。

Java鎖之自旋鎖理論知識

Unsafe類+CAS思想(自旋鎖)
Java鎖之自旋鎖代碼驗證


如果是第一次進來線程,就不進循環
A線程進來,發現期望的是空的,那么while的條件就是false,於是不進入循環,直接拿到了鎖。
B線程進來,發現期望的值不是空,那么while的條件就是true,於是它進入鎖中,一直會循環的判斷,直到期望的值是空了,才能推出循環,獲得鎖。

BB等5秒鍾,等A解鎖了,B才能解鎖
Java鎖之讀寫鎖理論知識


Java鎖之讀寫鎖代碼驗證

以前使用鎖和synchronized讀和寫通通不能並發執行,數據一致量可以保證,但並發性急劇下降。
線程操作資源類
高內聚低耦合





鎖不能進行細粒度的划分,只能把全部進行封殺



CountDownLatch/CyclicBarrier/Semaphore使用過嗎?

CountDownLatch

火箭發射





使用枚舉







CyclicBarrier Demo
循環屏障
人都到齊了才能開會



一個是減法,倒計時
一個是加法,累積
Semaphore Demo
多個線程強多個資源
信號燈
資源為1時就退化成synchronized

不寫是非公平鎖,效率高允許加塞
可以復用
可以搶占可以釋放

停3秒鍾
阻塞隊列知道嗎?

阻塞隊列理論

阻塞隊列接口結構和實現類
生產者 消費者 sychronized wait notify
隊列:先進先出
阻塞:

生產者消費者模式:用阻塞對壘寫








阻塞隊列API之拋出異常組








檢查隊列的首元素是誰?
a
先進先出
阻塞隊列API之返回布爾值組






隊列的首元素是誰

阻塞隊列API之阻塞和超時控制


阻塞的意思是:我現在滿了,就等着,直到有元素出去。因為我不能丟消息呀,就等着

取不出來就堵着
過時不候
超時

這時候沒有等2秒鍾
只阻塞2秒鍾。2秒鍾之后就打印出false
阻塞隊列之同步SynchronizedQueue隊列


0庫存,生產出來的馬上被別人拿走
你不消費,你想到里面插第二個你插不進去


線程通信之生產者消費者傳統版









防止虛假喚醒


沒有控制住,生產了2個
所以喚醒的時候要加入循環
synchronized和lock有什么區別



鎖綁定多個條件condition











線程通信之生產者消費者阻塞隊列版








線程池用過嗎?ThreadPoolExecutor談談你的理解?

Callable接口


區別:
runnable接口沒有返回值,不會拋異常,實現run
callable接口有返回值,會拋異常,實現call
要的是runnable,有的是callable,如何把它們之間加上關系
生活的例子:兩個同學,我默認接收的參數是張,但是,要找一個中間人



FutureTask實現穿針引線的作用。


已經有runnable接口,為什么還需要callable接口呢?
多個線程都要使用cllable,每次都要返回一個成功或者失敗的返回值。
適配模式。

get()建議放在最后
因為我們不會等這個線程,給它充足的時間去計算

如果把get放到前面,mian線程就被堵住了
所以這里可以加一個循環的判斷!等算完了,才往下做

兩個線程都開始做同一個任務,只會執行一次!即復用
如果說非要進去!那么就要啟動多個futuretask
線程池使用及優勢

永遠傳參數傳接口


看CPU的核數

線程池3個常用使用方式



ThreadPoolExecutor
輔助工具類



一池固定線程、一池一線程、一池多線程









線程池7大參數入門簡介







線程池7大參數深入介紹




線程池底層工作原理


線程池用過嗎?生產上你如何設置合理參數。

線程池的4種拒絕策略理論簡介



從而降低任務流量。
線程池實際中使用哪一個




無界的阻塞隊列
線程池的手寫改造和拒絕策略


最高幾個客戶來辦業務,就報拒絕了

默認的拒絕策略:馬上報異常
調用者運行機制:不會拋棄任務也不會拋出異常,而是將某些任務回退到調用者,從而降低新任務的流量
誰讓你調用我的,那么你就去找他
拋棄隊列中等待時間最久的任務
這樣的話只能完成8個人的業務,剩下的就直接丟棄了
線程池配置合理線程數




死鎖編碼及定位分析



單線程不可能有死鎖!沒人跟你搶,你怎么會死鎖。
兩把鎖
持有自己的鎖還妄圖得到別人的鎖。















