1.JMM內(nèi)存模型
JMM是JAVA的內(nèi)存模型芙委,是一種抽象模型并不真實(shí)存在
保證可見性:jvm在運(yùn)行時(shí)會(huì)為線程創(chuàng)建對(duì)應(yīng)的工作內(nèi)存第租,區(qū)別于主內(nèi)存即物理內(nèi)存,而java內(nèi)存模型中規(guī)定所有變量存儲(chǔ)在主內(nèi)存中拷获,被所有線程共享待榔。線程對(duì)變量的操作必須在工作內(nèi)存中進(jìn)行,所以線程需要將變量從主內(nèi)存拷貝到自己的工作內(nèi)存猎莲,然后對(duì)變量操作绍弟,操作完成后再將變量寫回主內(nèi)存,而不是直 接操作主內(nèi)存的變量著洼。各個(gè)線程中的工作內(nèi)存存儲(chǔ)著主內(nèi)存的變量副本拷貝樟遣,不同線程之間不能訪問對(duì)方的工作內(nèi)存,線程間的通訊必須通過(guò)主內(nèi)存完成身笤。當(dāng)線程A對(duì)變量M更改并寫回主內(nèi)存宣羊,其他線程第一時(shí)間同步主內(nèi)存中共享變量M的最新值的機(jī)制寥袭,就叫做JMM的可見性(所有線程對(duì)共享變量或資源的操作都在主內(nèi)存中 )。
不保證原子性:以變量累加為例,多個(gè)線程取到主內(nèi)存的變量A等缀,各自在工作內(nèi)存中累加后將變量寫回主內(nèi)存胃惜。這段操作在操作棧中分為多步除破,在最后一步寫入主內(nèi)存前并扇,線程M1被線程M2搶占cup控制權(quán),寫入M2的變量值并同步給其他線程從而丟失了線程M1被掛起前的操作(寫覆蓋)文搂。解決方案是使用具有原子性操作的包裝類代替變量A的類型适刀。
禁止指令重排:沒有數(shù)據(jù)依賴性的代碼在jvm中的調(diào)用順序默認(rèn)經(jīng)過(guò)優(yōu)化后執(zhí)行,從而導(dǎo)致多線程情況下對(duì)同一個(gè)變量A的操作結(jié)果出現(xiàn)不同的情況煤蹭。
場(chǎng)景1:
問題的答案是不可以蔗彤,因?yàn)橛袛?shù)據(jù)的依賴性。
場(chǎng)景2:
解決方案就是禁止指令重排疯兼,用volatile修飾變量A然遏。
volatile解決單例模式下線程安全問題:
2.CAS:比較并交換
通過(guò)比較,當(dāng)一個(gè)變量在其所在的工作內(nèi)存中的value和主內(nèi)存中的value一致的情況下才會(huì)把工作內(nèi)存中的value更新到主內(nèi)存吧彪,否則保留主內(nèi)存的value
再度理解CAS待侵,CAS是系統(tǒng)原語(yǔ),功能是判斷內(nèi)存中每個(gè)位置的值是否為預(yù)期值姨裸,如果是則改為新值秧倾,這個(gè)過(guò)程是原子性的。
并發(fā)原語(yǔ)體現(xiàn)在JAVA語(yǔ)言中就是rt包中sun.misc.Unsafe類中的各個(gè)方法傀缩。調(diào)用Unsafe類中的CAS方法那先,JVM會(huì)幫我們實(shí)現(xiàn)出CAS匯編指令。這是一種完全依賴硬件的功能赡艰,通過(guò)它實(shí)現(xiàn)原子操作售淡,即在執(zhí)行過(guò)程中不允許被打斷。
CAS的缺點(diǎn):
1.高并發(fā)環(huán)境循環(huán)時(shí)間長(zhǎng)揍堕,開銷大(一直在dowhile自旋判斷)
2.相比于sync修飾保證線程安全的方法,CAS只能保證數(shù)量是一的共享變量的原子操作
3.引發(fā)ABA問題:線程M1汤纸,M2都從主內(nèi)存中取出變量A衩茸,并且線程M2進(jìn)行了一些操作將變量A的值從最初的value1改成了value2并寫回主內(nèi)存,然后線程M2又將主內(nèi)存變量A的值由value2改成了value1并寫回主內(nèi)存贮泞,這時(shí)候線程M1進(jìn)行CAS操作時(shí)發(fā)現(xiàn)內(nèi)存中仍然是value1楞慈,線程M1的CAS操作成功。雖然線程M1的操作是成功的但是并不代表這個(gè)過(guò)程是沒有問題的啃擦。
除了AtomicInteger囊蓝,也可以通過(guò)AtomicReference自定義原子類(類的原子包裝):
ABA問題--重現(xiàn)
ABA問題--解決
非線程安全集合
HashSet同樣也是非線程安全的集合,原因同ArrayList议惰,解決方案對(duì)應(yīng)上面代碼示例的2和3方案,即Collections.synchronizedSet(hashSet)和Set<String> hashSet2 = new CopyOnWriteArraySet<>()
PS:1.CopyOnWriteArraySet的創(chuàng)建底層也是用的CopyOnWriteArrayList乡恕,由其構(gòu)造器可知:
public CopyOnWriteArraySet() {
al = new CopyOnWriteArrayList<E>();
}
2.HashSet的底層使用的HashMap言询,由其構(gòu)造器可知:
public HashSet() {
map = new HashMap<>();
},只不過(guò)set做add操作的時(shí)候傲宜,元素e作為map的key运杭,Object類型的常量PRESENT作為map的value,代碼可見:
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
對(duì)于HashMap同樣也是非線程安全的集合,解決方案:Collections.synchronizedMap(hashMap)和ConcurrentHashMap<String,String> hashMap = new ConcurrentHashMap<>();
3.copyonwritearraylist適用于數(shù)據(jù)量不大的場(chǎng)景函卒,不適用于數(shù)據(jù)量大的場(chǎng)景辆憔。由于寫操作的時(shí)候,需要拷貝數(shù)組报嵌,會(huì)消耗內(nèi)存虱咧,如果原數(shù)組的內(nèi)容比較多的情況下,可能導(dǎo)致young gc或者full gc锚国,適用于讀多寫少的場(chǎng)景腕巡,不適用于實(shí)時(shí)讀的場(chǎng)景。
如果寫操作未完成血筑,那么直接讀取原數(shù)組的數(shù)據(jù)绘沉;
如果寫操作完成,但是引用還未指向新數(shù)組豺总,那么也是讀取原數(shù)組數(shù)據(jù)车伞;
如果寫操作完成,并且引用已經(jīng)指向了新的數(shù)組喻喳,那么直接從新數(shù)組中讀取數(shù)據(jù)另玖。
公平鎖:多個(gè)線程按照申請(qǐng)鎖的順序來(lái)獲取鎖,類似排隊(duì)打飯,先來(lái)后到
非公平鎖:多個(gè)線程獲取鎖的順序并不是按照申請(qǐng)鎖的順序日矫,有可能后申請(qǐng)的線程比先申請(qǐng)的線程優(yōu)先獲取鎖赂弓,在高并發(fā)的情況下,有可能會(huì)造成優(yōu)先級(jí)反轉(zhuǎn)或者饑餓現(xiàn)象
并發(fā)包中ReentrantLock的創(chuàng)建可以指定構(gòu)造函數(shù)的boolean類型來(lái)得到公平鎖或非公平鎖哪轿,缺省是非公平鎖
公平鎖和非公平鎖的區(qū)別:
公平鎖在并發(fā)環(huán)境中每個(gè)線程在獲取鎖時(shí)會(huì)先查看此鎖維護(hù)的等待隊(duì)列盈魁,如果為空,或者當(dāng)前線程是等待隊(duì)列的第一個(gè)就占有鎖窃诉,否則就會(huì)加入到等待隊(duì)列中杨耙,以后會(huì)按照FIFO的規(guī)則從隊(duì)列中取到自己
非公平鎖上來(lái)就直接占有鎖,如果嘗試失敗就再采用類似公平鎖的方式
對(duì)于ReentrantLock而言飘痛,通過(guò)構(gòu)造函數(shù)指定該鎖是否是公平鎖珊膜,默認(rèn)是非公平鎖
對(duì)于Synchronized而言,也是非公平鎖宣脉。
非公平鎖相對(duì)公平鎖的優(yōu)點(diǎn)在于吞吐量比較大
可重入鎖(又名遞歸鎖)
一個(gè)線程獲取了外層的鎖O车柠,對(duì)于其加鎖控制的代碼中其他鎖I不必重新索取,好比一個(gè)人有了大門的鑰匙塑猖,對(duì)于房間內(nèi)上的鎖都有可進(jìn)入的權(quán)限
ReentrantLock和Synchronized都是可重入鎖
可重入鎖的最大的作用是避免死鎖
自旋鎖:嘗試獲取鎖的方式不是立即阻塞竹祷,而是通過(guò)循環(huán)訪問獲取鎖的方式,需要合理使用
可重入讀寫鎖(ReentrantReadWriteLock)
好處: 讀讀不能互斥羊苟,提升鎖性能塑陵,減少線程競(jìng)爭(zhēng)。
缺點(diǎn)是:當(dāng)讀鎖過(guò)多時(shí)候蜡励,寫鎖少令花,存在鎖饑餓現(xiàn)象。
JUC APIs
CountDownLatch:當(dāng)某個(gè)線程M1阻塞直到另一些線程完成一系列操作后才被喚醒凉倚,CountDownLatch有兩個(gè)方法兼都,await阻塞調(diào)用的線程M1;countdown會(huì)將計(jì)數(shù)器減1稽寒,當(dāng)計(jì)數(shù)器減到0時(shí)線程M1被喚醒繼續(xù)執(zhí)行
FeatureTask有類似CountDownLatch的效果:
線程通訊樣例:
CyclicBarrier:集齊七顆龍珠才能召喚神龍
Semaphore:多個(gè)線程搶奪多個(gè)資源
阻塞隊(duì)列
當(dāng)阻塞隊(duì)列是空時(shí)俯抖,從隊(duì)列中獲取元素的操作將會(huì)被阻塞
當(dāng)阻塞隊(duì)列是滿時(shí),往隊(duì)列中添加元素的操作將會(huì)被阻塞
使用阻塞隊(duì)列的好處:不需要關(guān)心什么時(shí)候阻塞線程瓦胎,什么時(shí)候喚醒線程
架構(gòu):
重點(diǎn)阻塞隊(duì)列:
ArrayBlockingQueue:數(shù)據(jù)結(jié)構(gòu)組成的有界阻塞隊(duì)列
LinkedBlockingQueue:鏈表結(jié)構(gòu)組成的有界(但默認(rèn)大小為Integer.MAX_VALUE)阻塞隊(duì)列
SynchronousQueue:只存儲(chǔ)單個(gè)元素的隊(duì)列(需要消費(fèi)一個(gè)才實(shí)時(shí)生產(chǎn)一個(gè))
阻塞隊(duì)列APIs:
Synchronized和Lock的區(qū)別
1.原始構(gòu)成:Synchronized是關(guān)鍵字屬于JVM層面芬萍,底層通過(guò)monitor對(duì)象來(lái)完成,wait和notify等方法依賴monitor對(duì)象搔啊,Lock是具體的類(java.util.concurrent.locks.Lock)是api層面的鎖
2.使用方法:Synchronized不需要手動(dòng)釋放鎖柬祠,當(dāng)Synchronized代碼執(zhí)行完后系統(tǒng)會(huì)自動(dòng)讓線程釋放對(duì)鎖的占用,Lock需要手動(dòng)釋放鎖并沒有主動(dòng)釋放鎖负芋,有可能導(dǎo)致出現(xiàn)死鎖的現(xiàn)象漫蛔,需要lock()和unlock()方法配合try catch finally代碼塊完成
3.等待是否可以中斷:Synchronized不可以中斷嗜愈,除非拋出異常或者正常運(yùn)行結(jié)束推出莽龟,Lock可以中斷:設(shè)置超時(shí)方法tryLock(long timeout,TimeUnit unit)或者lockInterruptibly()放代碼塊中蠕嫁,調(diào)用interrupt方法可以中斷
4.加鎖是否公平:Synchronized是非公平鎖,Lock兩者都可以毯盈,默認(rèn)是非公平鎖剃毒,構(gòu)造方法傳入boolean設(shè)置是否是公平鎖
5.鎖綁定多個(gè)條件Condition:Synchronized沒有,Lock可以精確喚醒而不是像Synchronized喚醒一個(gè)或者全部
6.非靜態(tài)方法的鎖默認(rèn)是this搂赋,靜態(tài)方法的鎖是對(duì)象對(duì)應(yīng)的Class
線程通訊
1.生產(chǎn)者消費(fèi)者
2.阻塞隊(duì)列-生產(chǎn)者消費(fèi)者
線程池
體系結(jié)構(gòu):
CallableDemo
線程池的主要工作是控制運(yùn)行的線程的數(shù)量赘阀,處理過(guò)程中將線程放入隊(duì)列,然后在線程創(chuàng)建后啟動(dòng)這些任務(wù)脑奠,如果線程數(shù)量超過(guò)了最大數(shù)量基公,超出數(shù)量的線程排隊(duì)等待,等其他線程執(zhí)行完畢后宋欺,再?gòu)年?duì)列中取出任務(wù)執(zhí)行
主要特點(diǎn)轰豆,線程服用減少系統(tǒng)開支,控制最大并發(fā)數(shù)量齿诞,管理線程
即
第一降低資源消耗酸休,通過(guò)重復(fù)利用已將創(chuàng)建的線程降低線程創(chuàng)建和銷毀造成的系統(tǒng)開銷
第二提高響應(yīng)速度,當(dāng)任務(wù)達(dá)到時(shí)掌挚,任務(wù)可以不需要等待線程創(chuàng)建就能立即執(zhí)行
第三提高線程的可管理性雨席,線程是稀缺資源菩咨,可以對(duì)線程統(tǒng)一分配調(diào)優(yōu)和監(jiān)控
線程池的創(chuàng)建都依賴ThreadPoolExecutor吠式,如newCachedThreadPool的創(chuàng)建
常用線程池
線程池參數(shù)
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
corePoolSize,線程池中常駐核心線程數(shù)
maximumPoolSize抽米,線程池能夠容納同時(shí)執(zhí)行的最大線程數(shù)特占,此值必須大于等于1
keepAliveTime,多余空閑線程的存活時(shí)間
當(dāng)前線程池?cái)?shù)量超過(guò)corePoolSize時(shí)云茸,當(dāng)空閑時(shí)間達(dá)到keepAliveTime時(shí)是目,多余空閑線程會(huì)被銷毀直到只剩下corePoolSize個(gè)線程為止
unit,keepAliveTime的單位
workQueue标捺,任務(wù)隊(duì)列懊纳,被提交但尚未被執(zhí)行的任務(wù)
threadFactory,表示生成線程池中工作線程的線程工廠,用于創(chuàng)建線程亡容,一般用默認(rèn)的即可
handler嗤疯,拒絕策略,表示當(dāng)隊(duì)列滿了并且工作線程大于等于線程池的最大線程數(shù)闺兢,對(duì)新的任務(wù)需求的處理方式
拒絕策略(4種):
如何合理配置線程池最大線程數(shù)量:
1.CPU密集型茂缚,即任務(wù)需要大量的運(yùn)算而沒有阻塞,CPU一直全速運(yùn)行,一般公式:CPU核數(shù)+1個(gè)線程的線程池(Runtime.getRuntime().availableProcessors()獲取處理器個(gè)數(shù))
2.IO密集型脚囊,即線程并不是一直在執(zhí)行任務(wù)龟糕,則應(yīng)配置盡可能多的線程,
如CPU核數(shù)*2或者CPU/(1-阻塞系數(shù))悔耘,阻塞系數(shù)在0.8-0.9之間
死鎖
查看java程序進(jìn)程編號(hào)命令:jps -l
查看死鎖問題命令:jstack 進(jìn)程號(hào)
注意:線程池不允許使用Excutors創(chuàng)建讲岁,而是通過(guò)ThreadPoolExector的方式,因?yàn)榍罢呤褂玫淖枞?duì)列的最大長(zhǎng)度Integer.MAX_VALUE,可能會(huì)堆積大量請(qǐng)求造成系統(tǒng)OOM
JVM
垃圾回收的一種策略:對(duì)象可達(dá)性分析淮逊,即對(duì)象引用關(guān)系可以和GCRoot聯(lián)通就是對(duì)象可達(dá)
可以作為GCRootd的對(duì)象:
1.虛擬機(jī)棧中引用的對(duì)象 2.方法區(qū)中的類靜態(tài)屬性引用對(duì)象
3.方法區(qū)中的常量引用對(duì)象 4.本地方法中的JNI(native方法)
JVM參數(shù):
1.標(biāo)準(zhǔn)參數(shù) 如-version -help -showversion
2.x參數(shù)(了解) 如-Xint 執(zhí)行模式 -Xcomp 編譯模式 -Xmixed 先編譯后執(zhí)行
3.xx參數(shù)(重點(diǎn))
jps查看java進(jìn)程
jinfo查看某一個(gè)java進(jìn)程是否啟用某一個(gè)jvm參數(shù)催首,示例如下
jinfo也可以查看某一個(gè)java進(jìn)程的某一個(gè)jvm參數(shù)的設(shè)置取值,示例如下
-Xms和-Xmx
查看jvm家底參數(shù):
常用jvm參數(shù)
其中-Xmn默認(rèn)堆內(nèi)存的1/3
典型jvm參數(shù)設(shè)置
堆內(nèi)存的分割
MinorGC的過(guò)程
-XX:+PrintGCDetails 打印程序運(yùn)行過(guò)程發(fā)生的GC詳情
強(qiáng)引用:只要有強(qiáng)引用依然指向一個(gè)對(duì)象泄鹏,就不會(huì)被垃圾回收郎任,就算出現(xiàn)了OOM也不回收(默認(rèn))
軟引用:內(nèi)存夠用的時(shí)候就保留,不夠用就回收
弱引用:只要垃圾回收备籽,不管jvm內(nèi)存夠不夠都會(huì)被回收
軟引用和弱引用的適用場(chǎng)景
WeakHashMap VS HashMap
虛引用
引用隊(duì)列:弱引用和虛引用在gc之后舶治,會(huì)存放到引用隊(duì)列
GCroot與四種引用關(guān)系:一定被回收的是弱引用和虛引用,不會(huì)被回收的是強(qiáng)音用车猬,看情況的是軟引用
StackOverFlowError和Out'O'fMemoryError是異常還是錯(cuò)誤霉猛?錯(cuò)誤!
模擬java.lang.StackOverflowError
模擬java.lang.OutOfMemoryError: Java heap space
模擬java.lang.OutOfMemoryError: GC overhead limit exceeded
模擬java.lang.OutOfMemoryError: Direct buffer memory
模擬java.lang.OutOfMemoryError: unable to create native thread
調(diào)整最大線程數(shù)配置
模擬java.lang.OutOfMemoryError: Metaspace
垃圾回收器(新生代是GC 養(yǎng)老代是FullGC)
查看默認(rèn)的垃圾回收器:java -XX:+PrintCommandLineFlags -version,查看
常用的垃圾垃圾處理器
========================
新生代:串行
新生代:并行
老年代:CMS
Serial Old
如何選擇垃圾收集器
G1
集合springboot的jvm參數(shù)設(shè)置
Linux命令
整機(jī)性能分析:top珠闰,主要看CPU和MEM以及LoadAverage(三個(gè)值分別代表1 5 15分鐘系統(tǒng)的負(fù)載均衡惜浅,平均值超過(guò)60%代表系統(tǒng)壓力過(guò)重),uptime是其簡(jiǎn)化版
CPU:vmstat -n 2 3 伏嗜,每個(gè)2秒采樣一次坛悉,一共采樣3次,結(jié)果集主要看procs和cpu承绸,其中pros中的r表示運(yùn)行和等待cup時(shí)間片的進(jìn)程數(shù)裸影,原則上整個(gè)系統(tǒng)的運(yùn)行隊(duì)列不能超過(guò)總核數(shù)的2倍,否則表示系統(tǒng)壓力過(guò)大军熏,pros中的b表示等待資源的進(jìn)程數(shù)轩猩,比如正在等待磁盤IO 網(wǎng)絡(luò)IO等;cpu中的us表示用戶進(jìn)程占用cpu的百分比荡澎,sy表示內(nèi)核進(jìn)程占用cpu的百分比均践,us和sy之和的參考值為80%,超過(guò)80%表示存在cpu不足或者需要優(yōu)化用戶進(jìn)程摩幔,id表示cpu的空閑率
CPU:mpstat -P ALL 2 3 ,查看全部進(jìn)程的cpu占用率彤委,每2秒采樣一次,采樣3次
pidstat -u 1 -p 進(jìn)程號(hào)热鞍,每一秒采樣一次葫慎,查看某個(gè)進(jìn)程的cpu的使用情況
內(nèi)存:free -m或者pidstat -r 采樣間隔時(shí)間 -p 進(jìn)程號(hào)
硬盤:df -h
磁盤IO:iostat -xdk 2 3 或者pidstat -d 采樣間隔時(shí)間 -p 進(jìn)程號(hào)
網(wǎng)絡(luò)IO:ifstat 1
cpu過(guò)高分析思路:
先用top命令找到cpu占比最高的情況
ps -ef或者jps進(jìn)一步定位哪個(gè)進(jìn)程導(dǎo)致 獲取pid
通過(guò)ps -mp 獲取的pid(如5101) -o THREAD衔彻,tid,time命令查看線程占用情況偷办,獲取占用cpu資源最大的線程id tid
將線程id轉(zhuǎn)換為小寫的16進(jìn)制格式 比如5102換成13ee 艰额,使用命令jstack 5101 | grep 13ee -A60
找到對(duì)應(yīng)的業(yè)務(wù)代碼后做相關(guān)代碼層面的分析
github
常用詞:watch:會(huì)持續(xù)收到該項(xiàng)目的動(dòng)態(tài)
fork,復(fù)制某個(gè)項(xiàng)目到自己的github倉(cāng)庫(kù)中
star椒涯,可以理解為點(diǎn)贊
clone柄沮,將項(xiàng)目下載到本地
follow,關(guān)注你感興趣的作者废岂,會(huì)收到他們的動(dòng)態(tài)
in:xxx關(guān)鍵詞 in:name(,readme,description)
點(diǎn)贊超過(guò)5000的項(xiàng)目:xxx關(guān)鍵詞 stars:>=5000
fork數(shù)超過(guò)500的項(xiàng)目:xxx關(guān)鍵詞 forks:>500
fork數(shù)量在100到200之間 并且 點(diǎn)贊數(shù)量在80到100之間:xxx關(guān)鍵詞 forks:100..200 stars:80..100
awesome xxx關(guān)鍵詞:搜索學(xué)習(xí)內(nèi)容
高亮顯示一行或者多行:github代碼地址+#L13(高亮13行代碼)\github代碼地址+#L13-L23(高亮13到23行代碼)
項(xiàng)目?jī)?nèi)搜索:英文字母t
搜索某區(qū)域某語(yǔ)言下活躍的用戶:location:beijing language:java
forkjoin框架:
-- END