Java高并發(fā)編程詳解:多線程與架構(gòu)設(shè)計(jì)
多線程基礎(chǔ)
快速認(rèn)識(shí)線程
-
線程的介紹
線程是CPU調(diào)度的基本單位,每個(gè)線程都有自己的局部變量表缓溅、程序計(jì)數(shù)器、聲明周期等
-
快速創(chuàng)建并啟動(dòng)一個(gè)線程
- 創(chuàng)建方式
- 繼承java.lang.Thread類
- 實(shí)現(xiàn)java.lang.Runnable接口
- 基于Java 8 Lambda簡(jiǎn)化創(chuàng)建
- ()->{具體內(nèi)容}
- 方法引用
- 創(chuàng)建具體操作
- 重寫run方法
- 啟動(dòng)方式
- 調(diào)用Thread的start方法
- 注意
- 啟動(dòng)新的線程蛮位,只有調(diào)用了Thread的start方法严蓖,才代表派生了一個(gè)新的線程,否則Thread和其他普通的Java對(duì)象沒有什么區(qū)別饥努,start方法是立即返回方法捡鱼,并不會(huì)讓程序陷入阻塞
- 使用Jconsole或jstack觀察線程
- 操作系統(tǒng)啟動(dòng)一個(gè)JVM時(shí),其實(shí)是啟動(dòng)一個(gè)進(jìn)程肪凛,而在該進(jìn)程里面啟動(dòng)了一個(gè)以上的線程堰汉,其中Thread-0這個(gè)線程是示例中創(chuàng)建的辽社。
- jconsole展示的線程截圖
- 打開jstack一閃而過伟墙,無法查看
- 創(chuàng)建方式
-
線程的聲明周期
- 狀態(tài)圖
- 問題
- 執(zhí)行了Thread的start方法就代表該線程已經(jīng)開始執(zhí)行了嗎翘鸭?
- 線程聲明周期的5個(gè)主要階段
- NEW
- 用關(guān)鍵字NEW創(chuàng)建一個(gè)Thread對(duì)象時(shí),此時(shí)它并不處于執(zhí)行狀態(tài)戳葵,因?yàn)闆]有調(diào)用start方法啟動(dòng)該線程就乓,那么線程的狀態(tài)為NEW狀態(tài)
- 可以切換的狀態(tài)
- 通過start方法進(jìn)入RUNNABLE狀態(tài)
- RUNNABLE
- 線程對(duì)象調(diào)用start方法進(jìn)入RUNNABLE狀態(tài)后,才真正的在JVM進(jìn)程中創(chuàng)建一個(gè)線程拱烁,線程啟動(dòng)后不會(huì)立刻得到執(zhí)行生蚁,取決于CPU的調(diào)度
- 線程具備的資格,但是并沒有真正的執(zhí)行戏自,而是等待CPU的調(diào)度
- 由于存在Running狀態(tài)邦投,所以不會(huì)直接進(jìn)入BLOCKED和TERMINATED狀態(tài),即使在線程的執(zhí)行邏輯中調(diào)用wait擅笔、sleep或者其他block的IO操作等志衣,也必須先獲得CPU才可以。
- 可以切換的狀態(tài)
- 意外終止進(jìn)入TERMINATED狀態(tài)
- RUNNING狀態(tài)
- RUNNING
- 一旦獲得CPU猛们,那么此時(shí)線程才能執(zhí)行自己的代碼邏輯
- RUNNING狀態(tài)的線程事實(shí)上也是RUNNABLE的念脯,反之不成立。
- 可以切換的狀態(tài)
- 直接進(jìn)入TERMINATED狀態(tài)弯淘,比如調(diào)用JDK的stop方法或者判斷某個(gè)邏輯標(biāo)識(shí)
- 進(jìn)入BLOCKED狀態(tài)绿店,比如調(diào)用了sleep,或者wait方法而加入waitSet中庐橙;進(jìn)行某個(gè)阻塞的IO操作假勿,比如網(wǎng)絡(luò)數(shù)據(jù)讀寫;獲取某個(gè)鎖資源态鳖,從而加入到該鎖的阻塞隊(duì)列中
- 進(jìn)入RUNNABLE狀態(tài)废登,由于CPU調(diào)度使該線程放棄執(zhí)行或者線程主動(dòng)調(diào)用yield方法,放棄CPU執(zhí)行權(quán)
- BLOCKED
- 線程進(jìn)入BLOCKED狀態(tài)的原因參考上面
- 可以切換的狀態(tài)
- 直接進(jìn)入TERMINATED狀態(tài)郁惜,比如調(diào)用stop方法或者意外死亡(jvm crash)
- 進(jìn)入RUNNABLE狀態(tài)堡距,比如線程阻塞的操作結(jié)束、線程完成指定時(shí)間的休眠兆蕉、wait中的線程被其他線程notify/notifyAll喚醒羽戒、線程獲得某個(gè)鎖資源、線程在阻塞過程中被打斷(比如其他線程調(diào)用了interrupt方法)
- TERMINATED
- TERMINATED是線程的最終狀態(tài)虎韵,在該狀態(tài)中線程將不會(huì)切換到其他任何狀態(tài)易稠。線程進(jìn)入TERMINATED狀態(tài),意味著該線程的整個(gè)生命周期結(jié)束
- 使線程進(jìn)入TERMINATED狀態(tài)的情況
- 線程正常運(yùn)行結(jié)束
- 線程運(yùn)行出錯(cuò)意外結(jié)束
- JVM crash包蓝,導(dǎo)致所有的線程都結(jié)束
- NEW
-
線程的start方法剖析
- 在start0方法中調(diào)用了線程的run方法
- 總結(jié)
- Thread被構(gòu)造后使NEW狀態(tài)驶社,threadStatus這個(gè)內(nèi)部屬性為0
- 不能啟動(dòng)線程超過一次企量,否則就會(huì)出現(xiàn)IllegalThreadStateException異常
- 線程啟動(dòng)后將會(huì)被加入到一個(gè)ThreadGroup中
- TERMINATED狀態(tài)的線程再次調(diào)用start方法是不允許的,即TERMINATED狀態(tài)沒有辦法回到RUNNABLE/RUNNING狀態(tài)
- 演示
- 重復(fù)啟動(dòng)
- 線程運(yùn)行結(jié)束后再啟動(dòng)
- 都會(huì)拋出IllegalThreadStateException異常亡电,但是第一種情況中該線程處于運(yùn)行狀態(tài)届巩,而第二種情況是沒有該線程
-
Runnable接口的引入
- Runnable與Thread的優(yōu)缺點(diǎn)比較
- Runnable是接口,無需子類化
- Runnable與Thread的優(yōu)缺點(diǎn)比較
Thread構(gòu)造函數(shù)
- 構(gòu)造函數(shù)概覽
- 線程的名稱
- 為線程取特殊意義名字有助于問題的排查和線程的跟蹤
- 線程的默認(rèn)名稱
- "Thread-"+nextThreadNum()
- 給線程設(shè)置名稱
- 通過構(gòu)造函數(shù)
- 修改線程的名稱
- 在線程啟動(dòng)之前還可以對(duì)其進(jìn)行修改份乒,一旦線程啟動(dòng)恕汇,名字將不再被修改
- 沒明白NEW非NEW狀態(tài)的處理
- 線程的父子關(guān)系
- 在構(gòu)造函數(shù)的init方法中有體現(xiàn)
- 一個(gè)線程的創(chuàng)建肯定是由另一個(gè)線程完成的
- 被創(chuàng)建線程的父線程是創(chuàng)建它的線程
- ThreadGroup
- 如果在構(gòu)造Thread的時(shí)候沒有顯示的指定一個(gè)ThreadGroup,那么子線程將被加入父線程所在的線程組
- main線程所在的ThreadGroup稱為main
- 構(gòu)造一個(gè)線程的時(shí)候如果沒有顯示地指定ThreadGroup或辖,那么他將會(huì)和父線程同屬于一個(gè)ThreadGroup
- Thread與Runnable
- Thread負(fù)責(zé)線程本身相關(guān)地職責(zé)和控制瘾英,而Runnable負(fù)責(zé)邏輯執(zhí)行單元的部分
- Thread與JVM虛擬機(jī)棧
- Thread與stackSize
- 影響遞歸的深度,采用默認(rèn)值極客
- JVM內(nèi)存結(jié)構(gòu)
- 內(nèi)存結(jié)構(gòu)圖
- 程序計(jì)數(shù)器
- 書上是錯(cuò)的吧
- 程序計(jì)數(shù)器是線程私有的
- Java虛擬機(jī)棧
- Java虛擬機(jī)棧是線程私有的颂暇,它的生命周期與線程相同缺谴,是在JVM運(yùn)行時(shí)鎖創(chuàng)建的,在線程中耳鸯,方法在執(zhí)行的時(shí)候都會(huì)創(chuàng)建一個(gè)名為棧幀的數(shù)據(jù)結(jié)構(gòu)湿蛔,棧幀中主要存放局部變量表、操作棧片拍、動(dòng)態(tài)鏈接煌集、方法出口等信息
- 本地方法棧
- JVM為本地方法所劃分出的內(nèi)存區(qū)域
- Java中提供了調(diào)用本地方法的接口(Java Native Interface),也就是C/C++程序捌省,比如網(wǎng)絡(luò)通信苫纤、文件操作的底層,JVM為本地方法所劃分的內(nèi)存區(qū)域便是本地方法棧纲缓,這塊內(nèi)存區(qū)域完全由不同的JVM廠商實(shí)現(xiàn)
- 本地方法棧也是線程私有的
- 堆內(nèi)存
- 堆內(nèi)存是JVM中最大的一塊內(nèi)存區(qū)域卷拘,被所有的線程共享,Java在運(yùn)行期間創(chuàng)建的所有對(duì)象幾乎都存放在該內(nèi)存區(qū)域祝高,該該內(nèi)存區(qū)域也是垃圾回收的重點(diǎn)區(qū)域
- 堆區(qū)的分代劃分
- 方法區(qū)
- 方法是被多個(gè)線程所共享的內(nèi)存區(qū)域栗弟,它主要用于存儲(chǔ)已經(jīng)被虛擬機(jī)加載的類信息、常量工闺、靜態(tài)變量乍赫、即時(shí)編譯器(JIT)編譯后的代碼等數(shù)據(jù)
- 不同的JVM會(huì)有不同的實(shí)現(xiàn)
- Java 8元空間
- jstat
- Thread與虛擬機(jī)棧
- 程序計(jì)數(shù)器是比較小的一塊內(nèi)存,而且該部分內(nèi)存是不會(huì)出現(xiàn)任何溢出異常的陆蟆。與線程創(chuàng)建雷厂、運(yùn)行、銷毀等關(guān)系比較大的是虛擬機(jī)棧內(nèi)存了叠殷,而且占內(nèi)存劃分的大小將直接決定在一個(gè)JVM進(jìn)程中可以創(chuàng)建多少個(gè)線程
- 線程的創(chuàng)建數(shù)量是隨著虛擬機(jī)棧內(nèi)存的增多而減少的
- 虛擬機(jī)棧內(nèi)存是線程私有的改鲫,就是說每個(gè)線程都會(huì)占有指定的內(nèi)存大小,粗略認(rèn)為一個(gè)Java進(jìn)程的內(nèi)存大小為:堆內(nèi)存+線程數(shù)量*棧內(nèi)存
- 操作系統(tǒng)中,一個(gè)進(jìn)程的最大內(nèi)存是有限制的像棘,32為的Windows操作系統(tǒng)所允許的最大進(jìn)程內(nèi)存為2GB稽亏。可以根據(jù)上面公式可以得出缕题,線程數(shù)量與占內(nèi)存的大小是反比關(guān)系截歉,線程數(shù)量與堆內(nèi)存也是反比關(guān)系
- linux下還沒查到資料
- 計(jì)算線程數(shù)量的公式
- 線程數(shù)量=(最大地址空間)-JVM堆內(nèi)存-ReservedOsMemory)/ThreadStackSize
- ReservedOsMemory為系統(tǒng)保留內(nèi)存,32位Windows下一般位136MB左右
- 線程數(shù)量與操作系統(tǒng)的一些內(nèi)核配置有關(guān)
- linux下
- /proc/sys/kernel/threads-max
- /proc/sys/kernel/pid_max
- /proc/sys/vm/max_map_count
- linux下
- 程序計(jì)數(shù)器是比較小的一塊內(nèi)存,而且該部分內(nèi)存是不會(huì)出現(xiàn)任何溢出異常的陆蟆。與線程創(chuàng)建雷厂、運(yùn)行、銷毀等關(guān)系比較大的是虛擬機(jī)棧內(nèi)存了叠殷,而且占內(nèi)存劃分的大小將直接決定在一個(gè)JVM進(jìn)程中可以創(chuàng)建多少個(gè)線程
- Thread與stackSize
- 守護(hù)線程
- 守護(hù)線程一般用于處理一些后臺(tái)的工作避除,比如垃圾回收
- The Java Virtual Machine exits when the only threads running are all daemon threads
- 是說在正常退出的情況下怎披,而不是調(diào)用System.exit()
- 調(diào)用setDaemon方法設(shè)置守護(hù)線程胸嘁,true代表守護(hù)線程瓶摆,false代表正常線程
- 只在線程啟動(dòng)之前才能生效
- isDaemon方法判斷線程是不是守護(hù)線程
Thread API
-
sleep
- sleep介紹
- 構(gòu)造方法
- public static native void sleep(long millis) throws InterruptedException
- public static void sleep(long millis, int nanos)
throws InterruptedException
- sleep方法會(huì)使當(dāng)前線程進(jìn)入指定毫秒數(shù)的休眠,暫停執(zhí)行性宏,但是線程不會(huì)放棄monitor所的所有權(quán)
- Thread.sleep只會(huì)導(dǎo)致當(dāng)前線程進(jìn)入指定時(shí)間的休眠
- 構(gòu)造方法
- 使用TimeUnit替代Thread.sleep
- 可以省區(qū)時(shí)間單位的換算
- sleep介紹
-
yield
- yield方法介紹
- 提示調(diào)度器當(dāng)前線程愿意放棄處理器的當(dāng)前使用群井。調(diào)度器可以自由的忽略這個(gè)提示
- 調(diào)用yield方法會(huì)使當(dāng)前線程從RUNNING切換到RUNNABLE狀態(tài)
- yield與sleep的區(qū)別
- sleep會(huì)導(dǎo)致當(dāng)前線程暫停指定的時(shí)間,沒有CPU時(shí)間片的消耗
- yield只是堆CPU調(diào)度器的一個(gè)提示毫胜,如果CPU調(diào)度器沒有忽略這個(gè)提示书斜,它會(huì)導(dǎo)致線程上下文的切換
- sleep會(huì)使線程短暫block,會(huì)在給定的時(shí)間內(nèi)釋放CPU資源
- yield會(huì)使RUNNING狀態(tài)的線程進(jìn)入RUNNABLE狀態(tài)(如果CPU調(diào)度器沒有忽略這個(gè)提示的話)
- sleep幾乎百分之百的完成給定時(shí)間的休眠酵使,而yield的提示并不能一定擔(dān)保
- 最后一個(gè)沒明白
- yield方法介紹
-
設(shè)置線程優(yōu)先級(jí)
- 相關(guān)方法
- setPriority(int)
- getPriority()
- 線程優(yōu)先級(jí)介紹
- 優(yōu)先級(jí)搞的線程不一定有優(yōu)先被CPU調(diào)度的機(jī)會(huì)荐吉,因?yàn)樗皇且粋€(gè)提示操作
- 一般情況下不會(huì)對(duì)線程設(shè)定優(yōu)先級(jí)別
- 相關(guān)方法
-
獲取線程ID
- getId()獲取線程的唯一ID,線程的ID在整個(gè)JVM進(jìn)程中都是唯一的口渔,并且是從0開始逐次遞增
- 如果在main線程中創(chuàng)建一個(gè)唯一的線程样屠,并且調(diào)用getId()后發(fā)現(xiàn)其并不等于0,是因?yàn)橐粋€(gè)JVM進(jìn)程啟動(dòng)的時(shí)候缺脉,實(shí)際上是開辟了很多個(gè)線程
-
獲取當(dāng)前線程
- currentThread()用于返回當(dāng)前執(zhí)行線程的引用
-
設(shè)置線程上下文類加載器
- getContextClassLoader()獲取線程上下文的類加載器痪欲,即這個(gè)線程是由哪個(gè)類加載器加載的,如果是在沒有修改線程上下文類加載器的情況下攻礼,則保持與父線程同樣的類加載器
- setContextClassLoader(ClassLoader)設(shè)置該線程的類加載器
-
線程interrupt
中斷這個(gè)線程
-
相關(guān)方法
- public void interrupt()
- public static boolean interrupted()
- public boolean isInterrupted()
-
interrupt
-
讓當(dāng)前線程進(jìn)入阻塞狀態(tài)的方法
Object的wait方法
Object的wait(long)方法
Object的wait(long, int)方法
Thread的sleep(long)方法
Thread的sleep(long,int)方法
Thread的join方法
Thread的join(long)方法
Thread的join(long, int)方法
InterruptibleChannel的io操作
Selector的wakeup方法
其他方法 調(diào)用當(dāng)前線程的interrupt方法可以打斷阻塞
一旦線程在阻塞的情況下被打斷业踢,都會(huì)拋出一個(gè)成為InterruptedException的異常,這個(gè)異常作為信號(hào)會(huì)通知當(dāng)前線程被打斷了
在一個(gè)線程內(nèi)部存在著名為interrupt flag的標(biāo)識(shí)礁扮,如果一個(gè)線程被interrupt知举,那么它的flag將被設(shè)置,但是如果當(dāng)前線程正在執(zhí)行可中斷方法被阻塞時(shí)太伊,調(diào)用interrupt方法將其中斷雇锡,反而會(huì)導(dǎo)致flag被清除
-
-
isInterrupted
- 判斷當(dāng)前線程是否被中斷,只是對(duì)interrupt標(biāo)識(shí)的一個(gè)判斷
-
interrupted
- 用于判斷當(dāng)前線程是否被中斷倦畅,并且直接擦掉線程的interrupt標(biāo)識(shí)遮糖。如果當(dāng)前線程被打斷了,那么第一次調(diào)用interrupted方法會(huì)返回true叠赐,并且立即擦除interrupt標(biāo)識(shí)欲账;第二次包括以后的調(diào)用永遠(yuǎn)都會(huì)返回false
可中斷方法和不可中斷方法
-
join
等待這個(gè)線程死亡
join方法會(huì)使當(dāng)前線程永遠(yuǎn)等待下去屡江,直到期間被另外的線程中斷,或者join的線程執(zhí)行結(jié)束
-
重載方法
public final void join() throws InterruptedException
public final synchronized void join(long millis)
throws InterruptedException
public final synchronized void join(long millis, int nanos)
throws InterruptedException
-
如何關(guān)閉線程
- 棄用的stop方法在關(guān)閉線程時(shí)可能不會(huì)釋放掉monitor的所
- 幾種關(guān)閉線程的方法
- 正常關(guān)閉
- 線程結(jié)束生命周期而正常結(jié)束
- 捕獲中斷信號(hào)關(guān)閉線程
- 使用volatile開關(guān)控制赛不?
- 異常退出
- 進(jìn)程假死
- 線程阻塞
- 死鎖
- 借助jstack惩嘉、jconsole、jvisualvm等工具診斷
- 正常關(guān)閉
線程安全與數(shù)據(jù)同步
- 基本概念
- 共享資源是指多個(gè)線程同時(shí)訪問的資源
- 數(shù)據(jù)同步是指如何保證毒功而線程訪問到的數(shù)據(jù)是一致的
- 數(shù)據(jù)同步
- a++問題
- synchronized
- 初始synchronized關(guān)鍵字
- 什么是synchronized
- 定義
- synchronized關(guān)鍵字實(shí)現(xiàn)一種簡(jiǎn)單的策略踢故,用于防止線程干擾和內(nèi)存一致性錯(cuò)誤:如果一個(gè)對(duì)象對(duì)超過一個(gè)線程可見文黎,對(duì)那個(gè)獨(dú)享的變量的讀寫會(huì)通過同步的方法完成
- 具體表現(xiàn)
- synchronized關(guān)鍵字提供了一種鎖的機(jī)制,能確保共享變量的互斥訪問殿较,從而防止數(shù)據(jù)不一致問題的出現(xiàn)
- synchronized關(guān)鍵字包括monitor enter和monitor exit兩個(gè)JVM指令耸峭,它能保證在任何時(shí)候任何線程執(zhí)行到monitor enter成功之前都必須從主內(nèi)存中獲取數(shù)據(jù),而不是從緩存中淋纲,在monitor exit運(yùn)行成功后劳闹,共享變量被更新后的值必須刷入主存
- synchronized的指令嚴(yán)格遵守java happens-before規(guī)則,一個(gè)monitor exit指令之前必定要由一個(gè)monitor enter
- 定義
- synchronized關(guān)鍵字的用法
- synchronized可以用于對(duì)代碼塊或方法進(jìn)行修飾洽瞬,而不能夠用于對(duì)class以及變量進(jìn)行修飾
- 同步方法
- [default|public|private|proteced] synchronized [static] type method()
- public synchronized void synch(){}
- 同步代碼塊
- private final Object MUTEX = new Object(); public void sync(){ synchronized(MUTEX){}}
- 同步方法
- synchronized可以用于對(duì)代碼塊或方法進(jìn)行修飾洽瞬,而不能夠用于對(duì)class以及變量進(jìn)行修飾
- 什么是synchronized
- 深入synchronized關(guān)鍵字
- synchronized(mutex)是某線程獲取與mutext關(guān)聯(lián)的monitor鎖
- 線程堆棧分析
- jconsole檢查線程的狀態(tài)
- JVM指令分析
- javap -c查看匯編代碼
- monitorenter
- 每個(gè)對(duì)象都與一個(gè)monitor的lock鎖關(guān)聯(lián)本涕,一個(gè)monitor的lock的鎖只能被一個(gè)線程在同一時(shí)間獲得,在一個(gè)線程嘗試獲得與對(duì)象關(guān)聯(lián)monitor的所有權(quán)時(shí)會(huì)發(fā)生如下幾件事情
- 如果monitor的計(jì)數(shù)器位0伙窃,則意味著該monitor的lock還沒有被獲得菩颖,某個(gè)線程獲得之后立即對(duì)該計(jì)數(shù)器加一,從此該線程就是這個(gè)monitor的所有者了
- 如果一個(gè)已經(jīng)擁有該monitor所有權(quán)的線程重入为障,則會(huì)導(dǎo)致monitor計(jì)數(shù)器再次累加
- 如果monitor已經(jīng)被其他線程所擁有晦闰,則其他線程嘗試獲取該monitor的所有權(quán)時(shí),會(huì)被陷入阻塞狀態(tài)直到monitor的計(jì)數(shù)器變?yōu)?产场,才能再次嘗試獲取對(duì)monitor的所有權(quán)
- 每個(gè)對(duì)象都與一個(gè)monitor的lock鎖關(guān)聯(lián)本涕,一個(gè)monitor的lock的鎖只能被一個(gè)線程在同一時(shí)間獲得,在一個(gè)線程嘗試獲得與對(duì)象關(guān)聯(lián)monitor的所有權(quán)時(shí)會(huì)發(fā)生如下幾件事情
- monitorexit
- 釋放monitor的所有權(quán)鹅髓,就是將monitor的計(jì)數(shù)器減一,如果計(jì)數(shù)器的結(jié)果為0京景,該線程不再擁有對(duì)該monitor的所有權(quán)窿冯,即解鎖。與此同時(shí)被該monitor blcok的線程將再次嘗試獲得對(duì)該monitor的所有權(quán)
- monitorenter
- javap -c查看匯編代碼
- 使用synchronized需要注意的問題
- 與monitor關(guān)聯(lián)的對(duì)象不能位空确徙,即mutext不能位null
- synchronized作用域太大醒串。因?yàn)閟ynchronized關(guān)鍵字具有互斥行為,作用域越大鄙皇,會(huì)失去并發(fā)優(yōu)勢(shì)芜赌。synchronized關(guān)鍵字應(yīng)該盡可能地只作用域共享資源(數(shù)據(jù))地讀寫作用域
- 不同monitor企圖所相同的方法。即聲明mutext要static伴逸,不能忘掉
- 正確:private final Object mutex = new Object();
- 錯(cuò)誤:private final Object MUTEX = new Object();每個(gè)線程會(huì)獨(dú)立持有鎖
- 多個(gè)鎖的交叉導(dǎo)致死鎖
- 初始synchronized關(guān)鍵字
- This Monitor和Class Monitor
- 使用synchronized關(guān)鍵字同步類的不同實(shí)例方法缠沈,爭(zhēng)搶的是同一個(gè)monitor的lock,與之關(guān)聯(lián)的引用則是ThisMonitor的實(shí)例引用
- 用synchronized同步某個(gè)類的不同靜態(tài)方法爭(zhēng)搶的也是同一個(gè)monitor的lock
- 程序死鎖的原因以及如何診斷
- 程序死鎖
- 交叉鎖可能導(dǎo)致程序出現(xiàn)死鎖
- 內(nèi)存不足
- 一問一答式的數(shù)據(jù)交換
- 數(shù)據(jù)庫鎖
- 文件鎖
- 死循環(huán)引起的鎖
- 難排查
- 程序死鎖
線程間通信
- 線程間的通信與進(jìn)程間通信方式不同
- 同步阻塞與異步非阻塞
- 同步阻塞消息處理設(shè)計(jì)的缺點(diǎn)
- 客戶端等待時(shí)間過長(zhǎng)(提交Event時(shí)長(zhǎng)+接收Event創(chuàng)建thread時(shí)長(zhǎng)+業(yè)務(wù)處理時(shí)長(zhǎng)+返回結(jié)果時(shí)長(zhǎng))會(huì)陷入阻塞
- 由于客戶端提交的Event數(shù)量不多,導(dǎo)致系統(tǒng)同時(shí)受理業(yè)務(wù)數(shù)量有限洲愤,也就是系統(tǒng)整體的吞吐量不高
- 在業(yè)務(wù)達(dá)到峰值時(shí)颓芭,大量的業(yè)務(wù)處理線程阻塞會(huì)導(dǎo)致頻繁的CPU上下文切換,從而降低系統(tǒng)性能
- 頻繁的創(chuàng)建于銷毀線程柬赐,增加系統(tǒng)額外開銷
- 異步非阻塞消息處理
- 優(yōu)點(diǎn)
- 客戶端不用等結(jié)果處理結(jié)束就可以返回亡问,提供系統(tǒng)的吞吐量和并發(fā)量
- 服務(wù)端線程可以重復(fù)使用,減少了不斷創(chuàng)建線程帶來的資源浪費(fèi)
- 服務(wù)端的線程數(shù)量在一個(gè)可控制的范圍之內(nèi)肛宋,不會(huì)導(dǎo)致太多的CPU上下文切換
- 缺點(diǎn)
- 客戶端想要得到結(jié)果需要再次調(diào)用接口方法進(jìn)行查詢
- 優(yōu)點(diǎn)
- 同步阻塞消息處理設(shè)計(jì)的缺點(diǎn)
- 單線程間通信
- wait和notify實(shí)現(xiàn)生產(chǎn)者-消費(fèi)者模式
- wait和notify方法
- wait和notify方法并不是Thread特有的方法州藕,而是Object中的方法,即在JDK中每個(gè)類都擁有這兩個(gè)方法
- wait方法
- wait的重載方法
- public final void wait() throws InterruptedException
- public final native void wait(long timeout) throws InterruptedException
- public final void wait(long timeout, int nanos) throws InterruptedException
- wait的說明
- wait(0)代表著永不超時(shí)
- Object的wait(long timeout)方法會(huì)導(dǎo)致當(dāng)前線程進(jìn)入阻塞酝陈,直到其他線程調(diào)用了Object的notify或者notifyAll方法才將其喚醒床玻,或者阻塞時(shí)間到達(dá)了timeout時(shí)間而自動(dòng)喚醒
- wait方法必須擁有該對(duì)象的monitor,也就是必須在同步方法中使用
- 沒明白
- 當(dāng)前線程執(zhí)行了該對(duì)象的wait方法之后后添,將會(huì)放棄該monitor的所有權(quán)笨枯,并且進(jìn)入于該對(duì)象關(guān)聯(lián)的wait set中薪丁,即一旦線程執(zhí)行了某個(gè)object的wait方法之后遇西,它就會(huì)釋放該對(duì)象的monitor的所有權(quán),其他線程也有機(jī)會(huì)繼續(xù)爭(zhēng)搶該monitor的所有權(quán)
- wait的重載方法
- notify方法
- public final native void notify()
- noitfy的說明
- 喚醒單個(gè)正在執(zhí)行該對(duì)象wait方法的線程
- 如果有某個(gè)線程由于執(zhí)行該對(duì)象的wait方法而進(jìn)入阻塞則會(huì)被喚醒严嗜,如果沒有則會(huì)忽略
- 被喚醒的i安城需要重新獲取該對(duì)象所關(guān)聯(lián)monitor的lock才能繼續(xù)執(zhí)行
- wait和notify的注意事項(xiàng)
- wait方法是可中斷方法
- 線程執(zhí)行了某個(gè)對(duì)象的wait方法以后粱檀,會(huì)加入與之對(duì)應(yīng)的wait set中,每個(gè)對(duì)象的monitor都有一個(gè)與之關(guān)聯(lián)的wait set
- 當(dāng)線程進(jìn)入wait set之后漫玄,notify方法可以將其喚醒茄蚯,也就是從wait set中彈出,同時(shí)中斷wait中的線程也會(huì)將其喚醒
- 必須在同步方法中使用wait和notify方法睦优,因?yàn)閳?zhí)行wait和notify的前提條件是必須持有同步方法的monitory的所有權(quán)渗常,否則會(huì)拋出IllegalMonitorStateException
- 同步代碼的monitor必須于執(zhí)行wait notify方法的對(duì)象一致,即使用哪個(gè)對(duì)象的monitor進(jìn)行同步汗盘,就只能用哪個(gè)對(duì)象進(jìn)行性wait核notify皱碘。否則拋出IllegalMonitorStateExcpetion異常信息
- wait和sleep
- 類似點(diǎn)
- wait和sleep方法都可以使線程進(jìn)入阻塞狀態(tài)
- wait和sleep方法均是可中斷方法,被中斷后都會(huì)收到中斷異常
- 區(qū)別
- wait是Object的方法隐孽,而sleep是Thread特有的方法
- wait方法的執(zhí)行必須在同步方法中進(jìn)行癌椿,而sleep則不需要
- 線程在同步方法中執(zhí)行sleep方法時(shí),并不會(huì)釋放monitor的鎖菱阵,而wait方法則會(huì)釋放monitor的鎖
- sleep方法短暫休眠之后主動(dòng)退出阻塞踢俄,而wait方法(沒有指定wait時(shí)間)則需要被其他線程中斷后才能退出阻塞
- 類似點(diǎn)
- 多線程間通信
- 生產(chǎn)者消費(fèi)者
- notifyAll方法
- 多線程間線程通信需要用Object的notifyAll方法,可以喚醒由于調(diào)用wait方法而阻塞的線程晴及,但是noitfy方法只能喚醒其中的一個(gè)線程都办,而noitfyAll方法則可以同時(shí)喚醒全部的阻塞線程噪叙,同樣被喚醒的線程仍需要繼續(xù)爭(zhēng)搶monitor的鎖
- 生產(chǎn)者消費(fèi)者
- 前面代碼中會(huì)出現(xiàn)兩類問題
- LinkedList中沒有元素的時(shí)候仍舊調(diào)用removeFirst方法
- 當(dāng)LinkedList中的元素超過10個(gè)的時(shí)候仍舊執(zhí)行addLast方法
- 改進(jìn)
- 使用while和noitfyAll
- 前面代碼中會(huì)出現(xiàn)兩類問題
- notifyAll方法
- wait set
- 線程調(diào)用了某個(gè)對(duì)象的wait方法之后都會(huì)被加入與該對(duì)象monitor關(guān)聯(lián)的wati set中,并且釋放monitor的所有權(quán)
- 另外一個(gè)線程調(diào)用該monitor的notify或者notifyAll后讨跟,就會(huì)有thread從wait set中彈出
- 生產(chǎn)者消費(fèi)者
- 自定義顯示鎖BooleanLock
- synchronized關(guān)鍵字的缺陷
- synchronized提供一種互斥的數(shù)據(jù)同步機(jī)制稚叹,某個(gè)線程在獲取monitor lock的時(shí)候可能會(huì)被阻塞
- 無法控制阻塞時(shí)長(zhǎng)
- 阻塞不可被中斷
- 被synchronized同步的線程不可被中斷
- synchronized提供一種互斥的數(shù)據(jù)同步機(jī)制稚叹,某個(gè)線程在獲取monitor lock的時(shí)候可能會(huì)被阻塞
- synchronized關(guān)鍵字的缺陷
ThreadGroup詳解
- ThreadGroup并不是用來管理Thread的,而是針對(duì)Thread的一個(gè)組織結(jié)構(gòu)
- ThreadGroup與Thread
- 在Java程序中槽卫,默認(rèn)情況下跟压,新的線程都會(huì)被加入到main線程所在的group中,main線程的group名字同線程名字
- 創(chuàng)建ThreadGroup
- public ThreadGroup(String name)
- public ThreadGroup(ThreadGroup parent, String name)
- 復(fù)制Thread數(shù)組和ThreadGroup數(shù)組
- ThreadGroup操作
- ThreadGroup的基本操作
- activeCount()獲取group中活躍的線程歼培,這只是估值震蒋,不能百分百保證數(shù)字一定正確
- activeGroupCount()獲取group中活躍的子group
- getName()獲取group的名字
- getParent()
- list()該方法沒有返回值,執(zhí)行該方法會(huì)將group中所有的活躍線程信息去拿不輸出到控制臺(tái)
- ThreadGroup的interrupt
- interrupt一個(gè)thread group會(huì)導(dǎo)致該group中所有的active線程都被interrupt
- ThreadGroup的基本操作
Hook線程以及捕獲線程執(zhí)行異常
- 獲取線程運(yùn)行時(shí)異常
- Thread類中關(guān)于處理運(yùn)行時(shí)異常的4個(gè)API
- public void setUncaughtExceptionHandler(UncaughtExceptionHandler eh):為某個(gè)特定線程指定UncaughtExceptionHandler
- public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh):設(shè)置全局的UncaughtExceptionHandler
- public UncaughtExceptionHandler getUncaughtExceptionHandler():獲取特定線程的UncaughtExceptionHandler
- public static UncaughtExceptionHandler getDefaultUncaughtExceptionHandler():獲取全局的UncaughtExceptionHandler
- UncaughtExceptionHandler介紹
- 線程在執(zhí)行單元中是不允許拋出受檢異常的躲庄,派生它的線程將無法直接獲得它運(yùn)行中出現(xiàn)的異常信息
- 當(dāng)線程在運(yùn)行過程中出現(xiàn)異常時(shí)查剖,JVM會(huì)調(diào)用dispatchUncaughtException方法,該方法會(huì)將對(duì)應(yīng)線程實(shí)例以及異常信息傳遞給回調(diào)接口
- Thread類中關(guān)于處理運(yùn)行時(shí)異常的4個(gè)API
- 注入鉤子線程
- Hook線程介紹
- JVM進(jìn)程的退出是由于JVM進(jìn)程中沒有活躍的非守護(hù)線程噪窘,或者收到了系統(tǒng)中斷信號(hào)笋庄,向JVM程序注入一個(gè)Hook線程,在JVM進(jìn)程突出的時(shí)候倔监,Hook線程會(huì)啟動(dòng)執(zhí)行直砂,通過Runtime可以為JVM注入多個(gè)Hook線程
- 利用Hook防止重復(fù)啟動(dòng)
- Hook線程介紹
線程池原理以及自定義線程池
- 線程池原理
- 線程池實(shí)現(xiàn)
- 線程池的應(yīng)用
Java ClassLoader
ClassLoader主要負(fù)責(zé)加載class文件到JVM中,給定一個(gè)class的二進(jìn)制文件名浩习,ClassLoader會(huì)嘗試加載并且在JVM中生成構(gòu)成這個(gè)類的各個(gè)數(shù)據(jù)結(jié)構(gòu)静暂,然后使其分布在JVM對(duì)應(yīng)的內(nèi)存區(qū)域中
類的加載過程
- 類的加載過程簡(jiǎn)介
- 也有說5個(gè)階段的
- 加載階段
- 主要負(fù)責(zé)查找并且加載類的二進(jìn)制數(shù)據(jù)文件,即class文件
- 連接階段
- 驗(yàn)證
- 主要是確保類文件的正確性谱秽,比如class的版本洽蛀,class文件的魔術(shù)因子是否正確
- 準(zhǔn)備
- 為類的靜態(tài)變量分配內(nèi)存,并且為其初始化默認(rèn)值
- 解析
- 把類中的符號(hào)引用轉(zhuǎn)換為直接引用
- 驗(yàn)證
- 初始化階段
- 為類的靜態(tài)變量賦予正確的初始值(代碼編寫階段給定的值)
- 類的主動(dòng)使用和被動(dòng)使用
- JVM規(guī)范的6種主動(dòng)使用類的場(chǎng)景
- 通過new關(guān)鍵字會(huì)導(dǎo)致類的初始化
- 訪問類的靜態(tài)變量疟赊,包括讀取和更新會(huì)導(dǎo)致的初始化
- 訪問類的靜態(tài)方法郊供,會(huì)導(dǎo)致類的初始化
- 對(duì)某個(gè)類進(jìn)行反射操作,會(huì)導(dǎo)致類的初始化
- 初始化子類會(huì)導(dǎo)致父類的初始化
- 啟動(dòng)類近哟,就是執(zhí)行main函數(shù)所在的類會(huì)導(dǎo)致該類的初始化
- 6種情況外的都稱為被動(dòng)引用驮审,不會(huì)導(dǎo)致類的加載和初始化
- 注意事項(xiàng)
- 構(gòu)造某個(gè)類的數(shù)組時(shí)并不會(huì)導(dǎo)致該類的初始化
- 應(yīng)用類的靜態(tài)常量不會(huì)導(dǎo)致類的初始化
- JVM規(guī)范的6種主動(dòng)使用類的場(chǎng)景
- 類的加載過程詳解
- 類的加載階段
- 類的加載就是將class文件種的二進(jìn)制數(shù)據(jù)讀取到內(nèi)存中,將該字節(jié)流所代表的靜態(tài)存儲(chǔ)結(jié)構(gòu)轉(zhuǎn)換為方法區(qū)中運(yùn)行時(shí)的數(shù)據(jù)結(jié)構(gòu)椅挣,并且在堆內(nèi)存中生成一個(gè)該類的java.lang.Class對(duì)象头岔,作為訪問該方法去數(shù)據(jù)結(jié)構(gòu)的入口
- 類加載的最終產(chǎn)物就是堆內(nèi)存中的class對(duì)象,堆同一個(gè)ClassLoader來講鼠证,不管某個(gè)類被加載了多少次峡竣,對(duì)應(yīng)到堆內(nèi)存中的class對(duì)象始終是通一個(gè)
- 獲取類二進(jìn)制數(shù)據(jù)流的形式
- class二進(jìn)制文件
- 運(yùn)行時(shí)生成,比如通過開源的ASM包可以生成一些class量九,或者通過動(dòng)態(tài)代理java.lang.Proxy也可以生成代理類的二進(jìn)制字節(jié)流
- 通過網(wǎng)絡(luò)獲取适掰,比如Applet小程序颂碧,以及RMI動(dòng)態(tài)發(fā)布等
- 通過讀取zip文件獲得類的二進(jìn)制字節(jié)流,比如jar类浪、war(其實(shí)载城,jar和war使用的是和zip同樣的壓縮算法)
- 將類的二進(jìn)制數(shù)據(jù)存儲(chǔ)在數(shù)據(jù)庫的BLOB字段類型中
- 運(yùn)行時(shí)生成class文件,并且動(dòng)態(tài)加載费就,比如使用Thrift诉瓦、AVRO等都可以在運(yùn)行時(shí)將某個(gè)Schema文件生成對(duì)應(yīng)的若干個(gè)class文件,然后進(jìn)行加載
- 類的連接階段
- 驗(yàn)證
- 確保class文件的字節(jié)流所包含的內(nèi)容復(fù)合當(dāng)前JVM的規(guī)范要求力细,并且不會(huì)出現(xiàn)危害JVM自身安全的代碼睬澡,否則會(huì)拋出VerifyError之類的異常
- 驗(yàn)證的信息包括
- 驗(yàn)證文件格式
- 文件頭部存在著魔術(shù)因子,該因子確定這個(gè)文件到底是什么類型
- 主次版本號(hào)
- 構(gòu)成class文件的字節(jié)流是否存在缺失或者其他附加信息眠蚂,主要是看class的MD5指紋
- 常量池中的常量是否存在不被支持的變量類型煞聪,比如int64
- 指向常量中的引用是否指向了不存在的常量或者該常量的類型不被支持
- 元數(shù)據(jù)的驗(yàn)證
- 元數(shù)據(jù)的驗(yàn)證是對(duì)class的字節(jié)流進(jìn)行語義分析的過程,整個(gè)語義分析就是為了確保class字節(jié)流復(fù)合JVM規(guī)范的要求
- 具體驗(yàn)證
- 檢查這個(gè)類是否存在父類逝慧,是否實(shí)現(xiàn)了某個(gè)接口昔脯,這些父類何接口是否合法,或者是否存在
- 檢查該類是否繼承了final修飾的類笛臣,被修飾的類是不允許被繼承并且其中的方法是不允許被ovrride的
- 檢查該類是否為抽象類云稚,如果不是抽象類,那么它是否實(shí)現(xiàn)了父類的重選ing方法或接口中的所有方法
- 檢查方法重載的合法性捐祠,比如相同的方法名稱碱鳞、相同的參數(shù)但是返回類型不相同,這都不允許
- 字節(jié)驗(yàn)證碼
- 驗(yàn)證程序的控制流程踱蛀,比如循環(huán)、分支等
- 具體驗(yàn)證
- 保證當(dāng)前線程在程序計(jì)數(shù)器中的指令不會(huì)跳轉(zhuǎn)到不合法的字節(jié)碼指令中去
- 保證類型的轉(zhuǎn)換是合法的贵白,比如用A聲明的引用率拒,不能用B進(jìn)行強(qiáng)制類型轉(zhuǎn)換
- 保證任意時(shí)刻,虛擬機(jī)棧中的操作棧類型與指令代碼都能正確的被執(zhí)行
- 符號(hào)引用驗(yàn)證
- 驗(yàn)證符號(hào)引用轉(zhuǎn)換為直接引用時(shí)的合法性
- 具體驗(yàn)證
- 通過符號(hào)引用描述的字符串全限定名稱是否能夠順利地找到相關(guān)合法性
- 符號(hào)引用中的類禁荒、字段猬膨、方法,是否對(duì)當(dāng)前類可見呛伴,比如不能訪問引用類的私有方法
- 驗(yàn)證文件格式
- 準(zhǔn)備
- 當(dāng)一個(gè)class的字節(jié)流通過了所有的驗(yàn)證過程之后勃痴,就開始為該對(duì)象的類變量(靜態(tài)變量)分配內(nèi)存并且設(shè)置初始值了,類變量的內(nèi)存會(huì)被分配方法區(qū)中热康,不同于實(shí)例變量會(huì)被分配到堆內(nèi)存之中
- 初始值就是相應(yīng)類型在沒有被設(shè)置時(shí)的默認(rèn)值沛申,不同的數(shù)據(jù)類型其初始值不同
- 例子是細(xì)節(jié)
- 解析
- 在常量池中尋找類、接口姐军、字段和方法的符號(hào)引用铁材,并且將這些符號(hào)引用替換成直接用用的過程
- 驗(yàn)證
- 類的初始化階段
- 執(zhí)行<climit>()方法
- 只有當(dāng)接口中有變量的初始化操作時(shí)才會(huì)生成<clint>()方法
- 靜態(tài)語句塊只能對(duì)后面的靜態(tài)變量進(jìn)行賦值尖淘,但不能對(duì)其進(jìn)行訪問
- 執(zhí)行<climit>()方法
- 類的加載階段
JVM類加載器
- JVM內(nèi)置三大類加載器
- 類加載器的父親委托機(jī)制
- 根類加載器介紹
- 根加載器又稱為Bootstrap類加載器,該類加載器是最為頂層的加載器著觉,它沒有任何父加載器村生,它是由C++編寫的,主要負(fù)責(zé)虛擬機(jī)河西類庫的加載饼丘,比如整個(gè)java.lang包都是由根加載器所加載的趁桃,可以通過-Xbootclasspath來指定根加載器的路徑,也可以通過系統(tǒng)屬性來得知當(dāng)前JVM的根加載器都加載了哪些資源
- 擴(kuò)展類加載器介紹
- 擴(kuò)展類加載器的父加載器是根加載器肄鸽,它主要用于加載JAVA_HOME下的jre\lb\ext子目錄里面的類庫镇辉。擴(kuò)展類加載器是由純Java語言實(shí)現(xiàn)的,它是java.lang.URLClassLoader的子類贴捡,它的完整類名是sun.misc.Launcher$ExtClassLoader忽肛。擴(kuò)展類記載其所加載的類庫可以通過系統(tǒng)屬性java.ext.dir獲得
- 系統(tǒng)類加載器介紹
- 系統(tǒng)類加載器負(fù)責(zé)加載classpath下的類庫資源。我們?cè)谶M(jìn)行項(xiàng)目開發(fā)的時(shí)候引入的第三方j(luò)ar包烂斋,系統(tǒng)類加載器的父加載器是擴(kuò)展類加載器屹逛,同時(shí)它也是自定義類加載器的默認(rèn)父加載器,系統(tǒng)類加載器的加載路徑一般通過-classpath或者-cp指定汛骂,可以通過系統(tǒng)屬性java.class.path獲取
- 自定義類加載器
- 所有的自定義類加載器都是ClassLoader的直接子類或者間接子類罕模,java.lang.ClassLoader是一個(gè)抽象類,它里面沒有抽象方法帘瞭,但是有findClass方法淑掌,務(wù)必實(shí)現(xiàn)該方法,否則拋出ClassNotFoundException
- 類的全路徑幾種格式
- 雙親委托機(jī)制(父親委托機(jī)制)
- 當(dāng)一個(gè)類加載器被調(diào)用了loadClasss之后蝶念,它并不會(huì)直接將其加載抛腕,它并不會(huì)直接將其加載,而是先交給當(dāng)前類加載器的父加載器嘗試加載知道最頂層的父加載器媒殉,然后再一次向下進(jìn)行加載
- 子主題 2
- 類被加載后的內(nèi)存情況
- 子主題 1
線程上下文類加載器
- 為什么需要線程上下文類加載器
- 數(shù)據(jù)庫驅(qū)動(dòng)的初始化源碼分析
深入理解volatile關(guān)鍵字
volatile關(guān)鍵字介紹
- volatile關(guān)鍵字
- static修飾的字段不保證內(nèi)存可見性
- volatile關(guān)鍵字只能修飾類變量和實(shí)例變量担敌,對(duì)于方法參數(shù)、局部變量以及實(shí)例變量廷蓉,類常量都不能進(jìn)行修飾
- 機(jī)器硬件CPU
- CPU Cache模型
- 程序在運(yùn)行的過程中全封,會(huì)將所需要的數(shù)據(jù)從貯存復(fù)制一份到CPU Cache中,這樣CPU進(jìn)行計(jì)算時(shí)就可以直接對(duì)CPU Cache中的數(shù)據(jù)進(jìn)行讀取和寫入桃犬,當(dāng)運(yùn)算結(jié)束之后刹悴,再將CPU Cache中的最新數(shù)據(jù)刷新到主內(nèi)存當(dāng)中
- CPU通過Cache與主內(nèi)存進(jìn)行交互
- CPU緩存一致性問題
- 多線程i++的問題
- 多線程下,每個(gè)線程都有自己的工作內(nèi)存(本地內(nèi)存攒暇,對(duì)應(yīng)于CPU中的Cache
- 解決方案
- 通過總線加所的方式
- 通過緩存一致性協(xié)議
- 標(biāo)志Cache中的行無效
- CPU Cache模型
- Java內(nèi)存模型
- Java的內(nèi)存模型(JMM)指定了Java虛擬機(jī)如何與計(jì)算機(jī)的主內(nèi)存進(jìn)行工作
- Java的內(nèi)存模型決定了一個(gè)線程對(duì)共享變量的寫入何時(shí)對(duì)其他線程可見土匀,Java內(nèi)存模型定義了線程和主內(nèi)存之間的抽象關(guān)系
- 共享變量存儲(chǔ)與主內(nèi)存中,每個(gè)線程都可以范文
- 每個(gè)線程都有私有的工作內(nèi)存或者稱為本地內(nèi)存
- 工作內(nèi)存只存儲(chǔ)該線程對(duì)共享變量的副本
- 線程不能直接操作主內(nèi)存扯饶,只有先操作了工作內(nèi)存之后才能寫入主內(nèi)存
- 工作內(nèi)存和Java內(nèi)存模型都是抽象概念恒削,它涵蓋了緩存池颈、寄存器、編譯器優(yōu)化以及以減等
- 子主題 3
- Java內(nèi)存模型與CPU硬件架構(gòu)交互圖
深入volatile關(guān)鍵字
- 并發(fā)編程的三個(gè)重要特性
- 原子性
- 再一次的操作或者多次操作中钓丰,要么所有的操作全部都得到了執(zhí)行并且不會(huì)收到任何因素的干擾而中斷躯砰,要么所有的操作都不執(zhí)行
- 銀行轉(zhuǎn)賬
- 兩個(gè)原子性的操作結(jié)合在一起未必還是原子性的
- volatile關(guān)鍵字不保證數(shù)據(jù)的原子性,synchronized關(guān)鍵字保證携丁,自JDK 1.5版本起琢歇,提供的原子類型變量也可以保證原子性
- 再一次的操作或者多次操作中钓丰,要么所有的操作全部都得到了執(zhí)行并且不會(huì)收到任何因素的干擾而中斷躯砰,要么所有的操作都不執(zhí)行
- 有序性
- 程序代碼在執(zhí)行過程中的先后順序
- 由于Java在編譯器以及運(yùn)行期的優(yōu)化,處理器優(yōu)化梦鉴,導(dǎo)致了代碼的執(zhí)行順序未必就是開發(fā)者編寫代碼時(shí)的順序
- 指令重排
- 在單線程情況下李茫,無論怎樣的重排序最終都會(huì)保證程序的執(zhí)行結(jié)果和代碼順序執(zhí)行的結(jié)果是完全一致的,但是在多線程的情況下肥橙,如果有序性得不到保證魄宏,那么很有可能就會(huì)出現(xiàn)非常大的問題
- 程序代碼在執(zhí)行過程中的先后順序
- 可見性
- 當(dāng)一個(gè)線程對(duì)共享變量進(jìn)行了修改,那么另外的線程可以立即看到修改后的最新值
- 讀線程和寫線程的例子
- 當(dāng)一個(gè)線程對(duì)共享變量進(jìn)行了修改,那么另外的線程可以立即看到修改后的最新值
- 原子性
- JMM如何保證三大特性
- JVM采用內(nèi)存模型的機(jī)制來屏蔽各個(gè)平臺(tái)和操作系統(tǒng)之間內(nèi)存訪問的差異存筏,以實(shí)現(xiàn)讓Java程序在各種平臺(tái)下達(dá)到一致的內(nèi)存訪問效果
- JMM與原子性
- 對(duì)基本數(shù)據(jù)類型的變量讀取宠互、賦值操作都是原子性的
- 對(duì)引用類型變量的讀取和賦值操作也是原行星的
- x=4;y=x;y++;z=z+1
- 說明
- volatile關(guān)鍵字不具備保證原子性的語義
- JMM與可見性
- Java提供了三種方式來確蓖旨幔可見性
- volatile關(guān)鍵字具有保證可見性的語義
- JMM與有序性
- 在Java的內(nèi)存模型中予跌,允許編譯器和處理器對(duì)指令進(jìn)行重排序
- Java提供了三種保證有序性的方式
- Java內(nèi)存模型提供了Happens-before原則。如果兩個(gè)操作的執(zhí)行次序無法從happens-before原則推導(dǎo)出來善茎,那么它們就無法保證有序性券册,就是說虛擬機(jī)或者處理器可以隨意對(duì)它們進(jìn)行重排序處理
- happens-before原則
- 程序次序規(guī)則:在一個(gè)線程內(nèi),代碼按照編寫時(shí)的次序執(zhí)行垂涯,編寫在后面的操作發(fā)生于編寫在前面的從左之后
- 鎖定規(guī)則:一個(gè)unlock操作要先行發(fā)生于對(duì)同一個(gè)鎖的lock操作
- volatile變量規(guī)則:對(duì)一個(gè)變量的寫操作要早于對(duì)這個(gè)變量之后的讀操作
- volatile關(guān)鍵字具有保證有序性的語義
- 傳遞規(guī)則:如果操作A先于操作B烁焙,而操作B又現(xiàn)與操作C,則可以得出操作A肯定要現(xiàn)與操作C
- 線程啟動(dòng)規(guī)則:Thread對(duì)象的start()方法線性發(fā)生于該對(duì)象的任何動(dòng)作
- 線程中斷規(guī)則:對(duì)線程執(zhí)行interrupt()方法肯定要優(yōu)先于捕獲到中斷信號(hào)
- 線程終結(jié)規(guī)則:線程中所有的操作都要先行發(fā)生于線程的終止檢測(cè)
- 對(duì)象的終結(jié)規(guī)則:一個(gè)對(duì)象初始化的完成先行發(fā)生于finalize()方法之前
- happens-before原則
- volatile關(guān)鍵字深入解析
- volatile關(guān)鍵字的語義
- 保證了不同線程之間對(duì)共享變量操作時(shí)的可見性集币。當(dāng)一個(gè)線程修改volatile修飾的變量考阱,另外一個(gè)線程會(huì)立即看到最新值
- 禁止對(duì)指令進(jìn)行重排序
- volatile的原理和實(shí)現(xiàn)機(jī)制
- 偽代碼
- "lock;"前綴實(shí)際上相當(dāng)于一個(gè)內(nèi)存屏障,該內(nèi)存屏障會(huì)為指令的執(zhí)行提供如下幾個(gè)保障
- 子主題 1
- volatile的使用場(chǎng)景
- 開關(guān)控制
- 狀態(tài)標(biāo)記利用順序性
- Singleton涉及模式的double-check
- volatiel和synchronized
- 區(qū)別
- 使用上的區(qū)別
- volatile關(guān)鍵字只能用于修飾實(shí)例變量和類變量鞠苟,不能用于修飾方法以及方法參數(shù)和局部變量、常量等
- synchronized關(guān)鍵字不能用于對(duì)變量的修飾秽之,只能用于修飾方法或者語句塊
- volatile修飾的變量可以為null当娱,synchronized關(guān)鍵字同步語句塊的monitor對(duì)象不能為null
- 對(duì)原子性的保證
- volatile無法保證原子性
- 由于synchronized是一種排他的機(jī)制,因此synchronized關(guān)鍵字修飾的同步代碼是無法被中途打斷的考榨,因此它能保證代碼的原子性
- 對(duì)可見性的保證
- 兩者均可以保證共享資源在多線程間的可見性跨细,但是實(shí)現(xiàn)機(jī)制完全不同
- synchronized借助于JVM指令monitor enter和monitor exit對(duì)通過排他的方式使得同步代碼串行化,在monitor exit時(shí)所有共享資源都將會(huì)被刷新到主內(nèi)存中
- volatile使用機(jī)器指令"lock;"的方式迫使其他線程剛工作內(nèi)存中的數(shù)據(jù)失效河质,必須到主內(nèi)存中進(jìn)行再次加載
- 對(duì)有序性的保證
- volatile關(guān)鍵字進(jìn)行JVM編譯器以及處理器對(duì)其進(jìn)行重排序冀惭,所以他能保證有序性
- synchronized關(guān)鍵字鎖修飾的同步方法也可以保證順序性震叙,但是這種順序性是以程序的穿行化執(zhí)行換來的,在synchronized關(guān)鍵字鎖修飾的代碼塊中代碼指令也會(huì)發(fā)生指令重排序的
- 其他
- volatile不會(huì)使線程陷入阻塞
- synchronized關(guān)鍵字會(huì)使線程進(jìn)入阻塞狀態(tài)
- 使用上的區(qū)別
- 區(qū)別
- volatile關(guān)鍵字的語義
7種單例設(shè)計(jì)模式
- 思考維度
- 線程安全
- 高性能
- 懶加載
- 各種實(shí)現(xiàn)
- 餓漢模式
- 懶漢模式
- 懶漢+同步
- Double-Check
- Volatile+Double-Check
- Holder方式
- 枚舉方式
多線程設(shè)計(jì)架構(gòu)模式
監(jiān)控任務(wù)的生命周期
- 場(chǎng)景敘述
- Thread可以獲取狀態(tài)散休,但都是針對(duì)線程本身的媒楼,無法獲取提交的Runnable在運(yùn)行過程種所處的狀態(tài),如開始戚丸、結(jié)束划址、獲取結(jié)果。用傳入共享變量的方式會(huì)導(dǎo)致資源競(jìng)爭(zhēng)
- 當(dāng)觀察模式遇到Thread
- 當(dāng)某個(gè)對(duì)象發(fā)生狀態(tài)改變需要通知第三方的時(shí)候限府,觀察者模式特別適合夺颤。觀察者模式需要有事件源,也就是引發(fā)狀態(tài)改變的源頭 胁勺,Thread負(fù)責(zé)執(zhí)行任務(wù)的邏輯單元世澜,它清楚整個(gè)過程的始末周期,而事件的接收者則是通知接收者一方署穗。只需要將執(zhí)行任務(wù)的每一個(gè)階段都通知給觀察者即可
- 子主題 2
- 沒發(fā)現(xiàn)與硬編碼有啥區(qū)別
Single Thread Executor設(shè)計(jì)模式
- 機(jī)場(chǎng)過安檢
- 吃面問題
- 和只有一個(gè)線程的線程池有啥區(qū)別寥裂?
- 被synchronized侮辱了智商?
讀寫鎖分離設(shè)計(jì)模式
- 場(chǎng)景描述
- 讀寫分離程序設(shè)計(jì)
- 讀寫鎖的使用
- jdk中自帶的讀寫鎖蛇捌、Stamped鎖
- 樂觀鎖和悲觀鎖
不可變對(duì)象設(shè)計(jì)模式
- 線程安全性
- 不可變對(duì)象的設(shè)計(jì)
- String的實(shí)現(xiàn)
Future設(shè)計(jì)模式
- Future設(shè)計(jì)模式實(shí)現(xiàn)
- Future的使用以及技巧總結(jié)
- 增強(qiáng)FutureService使其支持回調(diào)
Guarded Suspension設(shè)計(jì)模式
- 什么是Guarded Suspension設(shè)計(jì)模式
- 示例
線程上下文設(shè)計(jì)模式
- 什么是上下文
- 線程上下文設(shè)計(jì)
- ThreadLocal詳解
- 使用ThreadLocal設(shè)計(jì)線程上下文
- VisualVM監(jiān)控堆內(nèi)存的代銷
- ThreadLocal內(nèi)存泄漏問題
balking設(shè)計(jì)模式
- 什么是Balking設(shè)計(jì)
- Balking模式只文檔編輯
Latch設(shè)計(jì)模式
- 什么是Latch
- CountDownLatch程序?qū)崿F(xiàn)
Thread-Per-Message設(shè)計(jì)模式
- 什么是Thread-Per-Message模式
- 每個(gè)任務(wù)一個(gè)線程
- 多用戶的網(wǎng)絡(luò)聊天
Two Phase Termination設(shè)計(jì)模式
- 什么是Two Phase Termination模式
- Two Phase Termination示例
- 知識(shí)擴(kuò)展
Worker-Thread設(shè)計(jì)模式
- 什么是Worker-Thread模式
- Worker-Thread模式實(shí)現(xiàn)
Active Objects設(shè)計(jì)模式
- 接受異步消息的主動(dòng)對(duì)象
- 標(biāo)準(zhǔn)Active Objects模式設(shè)計(jì)
- 通用Active Objects框架設(shè)計(jì)
Event Bus設(shè)計(jì)模式
- Event Bus設(shè)計(jì)
- Event Bus實(shí)戰(zhàn)
Event Driven設(shè)計(jì)模式
- Event-Driven Architecture基礎(chǔ)
- Event-Driven框架
- Event-Driven的使用