前言
只有光頭才能變強(qiáng)
之前在刷博客的時(shí)候睛约,發(fā)現(xiàn)一些寫得比較好的博客都會(huì)默默收藏起來叫惊。最近在查閱補(bǔ)漏,有的知識(shí)點(diǎn)比較重要的似舵,但是在之前的博客中還沒有寫到脚猾,于是趁著閑整理一下。?
文本的知識(shí)點(diǎn):
Integer常量池?
TCP拆包粘包?
select砚哗、poll龙助、epoll簡(jiǎn)單區(qū)別?
jdk1.6以后對(duì)Synchronize鎖優(yōu)化?
Java內(nèi)存模型
本文力求簡(jiǎn)單講清每個(gè)知識(shí)點(diǎn),希望大家看完能有所收獲【Java高級(jí)架構(gòu)進(jìn)階群】:836442475?本群提供免費(fèi)的學(xué)習(xí)指導(dǎo)频祝,架構(gòu)資料以及免費(fèi)的解答泌参,不懂得問題都可以在本群提出來,之后還會(huì)有職業(yè)生涯規(guī)劃以及面試指導(dǎo)常空;進(jìn)群修改群備注:開發(fā)年限-地區(qū)-經(jīng)驗(yàn)沽一,方便架構(gòu)師解答問題
點(diǎn)擊鏈接加入群聊【架構(gòu)華山論劍】:https://jq.qq.com/?_wv=1027&k=5piQOHC
一、神奇的Integer?
前陣子在群上看有人在討論關(guān)于Integer的true或者false問題漓糙,我本以為我已經(jīng)懂了這方面的知識(shí)點(diǎn)了铣缠。但還是做錯(cuò)了,后來去請(qǐng)教了一下朋友。朋友又給我發(fā)了另一張圖:
后來發(fā)現(xiàn)這是出自《深入理解Java虛擬機(jī)——JVM高級(jí)特性與最佳實(shí)踐(第2版)》中的10.3.2小節(jié)中~
public class Main_1 {?
public static void main(String[] args) {?
Integer a = 1;?
Integer b = 2;?
Integer c = 3;?
Integer d = 3;?
Integer e = 321;?
Integer f = 321;?
Long g = 3L;?
System.out.println(c == d);?
System.out.println(e == f);?
System.out.println(c == (a + b));?
System.out.println(c.equals(a + b));?
System.out.println(g == (a + b));?
System.out.println(g.equals(a + b));?
System.out.println(g.equals(a + h));?
}
}?
復(fù)制代碼你們可以先思考一下再往下翻看答案蝗蛙,看看能不能做對(duì)蝇庭。
1.1解題思路?
在解這道題之前,相信很多人都已經(jīng)知道了捡硅,在Java中會(huì)有一個(gè)Integer緩存池哮内,緩存的大小是:-128~127
答案是:
true?
false?
true?
true?
true?
false?
true
簡(jiǎn)單解釋一下:
使用==的情況:
如果比較Integer變量,默認(rèn)比較的是地址值壮韭。?
Java的Integer維護(hù)了從-128~127的緩存池?
如果比較的某一邊有操作表達(dá)式(例如a+b)北发,那么比較的是具體數(shù)值
使用equals()的情況:
無論是Integer還是Long中的equals()默認(rèn)比較的是數(shù)值。?
Long的equals()方法喷屋,JDK的默認(rèn)實(shí)現(xiàn):會(huì)判斷是否是Long類型
注意自動(dòng)拆箱琳拨,自動(dòng)裝箱問題。
反編譯一下看看:
import java.io.PrintStream;
public class Main_1 {?
public static void main(String[] paramArrayOfString) {?
Integer localInteger1 = Integer.valueOf(1);?
Integer localInteger2 = Integer.valueOf(2);?
Integer localInteger3 = Integer.valueOf(3);?
Integer localInteger4 = Integer.valueOf(3);?
Integer localInteger5 = Integer.valueOf(321);?
Integer localInteger6 = Integer.valueOf(321);?
Long localLong = Long.valueOf(3L);
復(fù)制代碼我使用的反編譯工具是jd-gui屯曹,如果還沒有試過反編譯的同學(xué)可以下載來玩玩:
github.com/java-decomp…
二狱庇、Synchronize鎖優(yōu)化手段有哪些*?
多線程文章回顧:
ThreadLocal就是這么簡(jiǎn)單?
多線程三分鐘就可以入個(gè)門了!?
Thread源碼剖析?
多線程基礎(chǔ)必要知識(shí)點(diǎn)恶耽!看了學(xué)習(xí)多線程事半功倍?
Java鎖機(jī)制了解一下?
AQS簡(jiǎn)簡(jiǎn)單單過一遍?
Lock鎖子類了解一下?
線程池你真不來了解一下嗎密任??
多線程之死鎖就是這么簡(jiǎn)單?
Java多線程打輔助的三個(gè)小伙子
之前在寫多線程文章的時(shí)候,簡(jiǎn)單說了一下synchronized鎖在jdk1.6以后會(huì)有各種的優(yōu)化:適應(yīng)自旋鎖偷俭,鎖消除批什,鎖粗化,輕量級(jí)鎖社搅,偏向鎖。
本以為這些優(yōu)化是非常難以理解的東西乳规,其實(shí)不然~~~簡(jiǎn)單了解一下還是很好理解的形葬。
2.1適應(yīng)自旋鎖?
鎖競(jìng)爭(zhēng)是kernal mode下的,會(huì)經(jīng)過user mode(用戶態(tài))到kernal mode(內(nèi)核態(tài)) 的切換暮的,是比較花時(shí)間的笙以。?
自旋鎖出現(xiàn)的原因是人們發(fā)現(xiàn)大多數(shù)時(shí)候鎖的占用只會(huì)持續(xù)很短的時(shí)間,甚至低于切換到kernal mode所花的時(shí)間冻辩,所以在進(jìn)入kernal mode前讓線程等待有限的時(shí)間猖腕,如果在此時(shí)間內(nèi)能夠獲取到鎖就避免了很多無謂的時(shí)間,若不能則再進(jìn)入kernal mode競(jìng)爭(zhēng)鎖恨闪。?
在JDK 1.6中引入了自適應(yīng)的自旋鎖倘感,說明自旋的時(shí)間不固定,要不要自旋變得越來越聰明咙咽。?
自旋鎖在JDK1.4.2中就已經(jīng)引入老玛,只不過默認(rèn)是關(guān)閉的,可以使用-XX:+UseSpinning參數(shù)來開啟,在JDK1.6中就已經(jīng)改為默認(rèn)開啟了蜡豹。?
參考資料:
自旋鎖和使線程休眠的非自旋鎖各有什么適用場(chǎng)景麸粮?www.zhihu.com/question/38…
2.2鎖消除?
如果JVM明顯檢測(cè)到某段代碼是線程安全的(言外之意:無鎖也是安全的),JVM會(huì)安全地原有的鎖消除掉镜廉!?
比如說:
復(fù)制代碼Vector是默認(rèn)加鎖的弄诲,但JVM如果發(fā)現(xiàn)vector變量?jī)H僅在vectorTest()方法中使用,那該vector是線程安全的娇唯。JVM會(huì)把vector內(nèi)部加的鎖去除齐遵,這個(gè)優(yōu)化就叫做:鎖消除。
2.3鎖粗化?
默認(rèn)情況下视乐,總是推薦將同步塊的作用范圍限制得盡量小洛搀。?
但是如果一系列的連續(xù)操作都對(duì)同一個(gè)對(duì)象反復(fù)加鎖和解鎖,甚至加鎖操作是出現(xiàn)在循環(huán)體中的佑淀,頻繁地進(jìn)行互斥同步操作也會(huì)導(dǎo)致不必要的性能損耗留美。?
JVM會(huì)將加鎖的范圍擴(kuò)展(粗化),這就叫做鎖粗化伸刃。
2.4輕量級(jí)鎖?
輕量級(jí)鎖能提升程序同步性能的依據(jù)是“對(duì)于絕大部分的鎖谎砾,在整個(gè)同步周期內(nèi)都是不存在競(jìng)爭(zhēng)的”,這是一個(gè)經(jīng)驗(yàn)數(shù)據(jù)捧颅。
如果沒有競(jìng)爭(zhēng)景图,輕量級(jí)鎖使用CAS操作避免了使用互斥量的開銷?
但如果存在鎖競(jìng)爭(zhēng),除了互斥量的開銷外碉哑,還額外發(fā)生了CAS操作挚币,因此在有競(jìng)爭(zhēng)的情況下,輕量級(jí)鎖會(huì)比傳統(tǒng)的重量級(jí)鎖更慢扣典。
簡(jiǎn)單來說:如果發(fā)現(xiàn)同步周期內(nèi)都是不存在競(jìng)爭(zhēng)妆毕,JVM會(huì)使用CAS操作來替代操作系統(tǒng)互斥量。這個(gè)優(yōu)化就被叫做輕量級(jí)鎖贮尖。
2.5偏向鎖?
偏向鎖就是在無競(jìng)爭(zhēng)的情況下把整個(gè)同步都消除掉笛粘,連CAS操作都不做了!
偏向鎖可以提高帶有同步但無競(jìng)爭(zhēng)的程序性能湿硝。它同樣是一個(gè)帶有效益權(quán)衡(Trade Off)性質(zhì)的優(yōu)化薪前,也就是說,它并不一定總是對(duì)程序運(yùn)行有利关斜,如果程序中大多數(shù)的鎖總是被多個(gè)不同的線程訪問示括,那偏向模式就是多余的。在具體問題具體分析的前提下痢畜,有時(shí)候使用參數(shù)-XX:-UseBiasedLocking來禁止偏向鎖優(yōu)化反而可以提升性能例诀。
2.6簡(jiǎn)單總結(jié)各種鎖優(yōu)化
自適應(yīng)偏向鎖:自旋時(shí)間不固定?
鎖消除:如果發(fā)現(xiàn)代碼是線程安全的随抠,將鎖去掉?
鎖粗化:加鎖范圍過小(重復(fù)加鎖),將加鎖的范圍擴(kuò)展?
輕量級(jí)鎖:在無競(jìng)爭(zhēng)的情況下使用CAS操作去消除同步使用的互斥量?
偏向鎖:在無競(jìng)爭(zhēng)環(huán)境下繁涂,把整個(gè)同步都消除拱她,CAS也不做。
參考資料:
blog.csdn.net/chenssy/art…
三扔罪、TCP粘包秉沼,拆包?
這是在看wangjingxin大佬面經(jīng)的時(shí)候看到的面試題,之前對(duì)TCP粘包矿酵,拆包沒什么概念唬复,于是就簡(jiǎn)單去了解一下。
3.1什么是拆包粘包全肮?為什么會(huì)出現(xiàn)敞咧??
在進(jìn)行Java NIO學(xué)習(xí)時(shí),可能會(huì)發(fā)現(xiàn):如果客戶端連續(xù)不斷的向服務(wù)端發(fā)送數(shù)據(jù)包時(shí)辜腺,服務(wù)端接收的數(shù)據(jù)會(huì)出現(xiàn)兩個(gè)數(shù)據(jù)包粘在一起的情況休建。?
TCP的首部格式:
TCP是基于字節(jié)流的,雖然應(yīng)用層和TCP傳輸層之間的數(shù)據(jù)交互是大小不等的數(shù)據(jù)塊评疗,但是TCP把這些數(shù)據(jù)塊僅僅看成一連串無結(jié)構(gòu)的字節(jié)流测砂,沒有邊界;?
從TCP的幀結(jié)構(gòu)也可以看出百匆,在TCP的首部沒有表示數(shù)據(jù)長(zhǎng)度的字段
基于上面兩點(diǎn)砌些,在使用TCP傳輸數(shù)據(jù)時(shí),才有粘包或者拆包現(xiàn)象發(fā)生的可能加匈。?
一個(gè)數(shù)據(jù)包中包含了發(fā)送端發(fā)送的兩個(gè)數(shù)據(jù)包的信息存璃,這種現(xiàn)象即為粘包
接收端收到了兩個(gè)數(shù)據(jù)包,但是這兩個(gè)數(shù)據(jù)包要么是不完整的雕拼,要么就是多出來一塊有巧,這種情況即發(fā)生了拆包和粘包
拆包和粘包的問題導(dǎo)致接收端在處理的時(shí)候會(huì)非常困難(因?yàn)闊o法區(qū)分一個(gè)完整的數(shù)據(jù)包)
3.2解決拆包和粘包?
分包機(jī)制一般有兩個(gè)通用的解決方法:
1,特殊字符控制?
2,在包頭首都添加數(shù)據(jù)包的長(zhǎng)度
如果使用netty的話,就有專門的編碼器和解碼器解決拆包和粘包問題了悲没。
tips:UDP沒有粘包問題,但是有丟包和亂序示姿。不完整的包是不會(huì)有的逊笆,收到的都是完全正確的包。傳送的數(shù)據(jù)單位協(xié)議是UDP報(bào)文或用戶數(shù)據(jù)報(bào)难裆,發(fā)送的時(shí)候既不合并子檀,也不拆分镊掖。
參考資料
blog.csdn.net/scythe666/a…—>TCP粘包褂痰,拆包及解決方法?
www.ideawu.net/blog/archiv…—>關(guān)于TCP粘包和拆包的終極解答
四、select缩歪、poll归薛、epoll簡(jiǎn)單區(qū)別?
NIO回顧:
JDK10都發(fā)布了,nio你了解多少匪蝙?
在Linux下它是這樣子實(shí)現(xiàn)I/O復(fù)用模型的:?
調(diào)用select/poll/epoll其中一個(gè)函數(shù),傳入多個(gè)文件描述符逛球,如果有一個(gè)文件描述符就緒,則返回幸海,否則阻塞直到超時(shí)屋厘。?
這幾個(gè)函數(shù)是有些區(qū)別的,可能有的面試官會(huì)問到這三個(gè)函數(shù)究竟有什么區(qū)別:?
區(qū)別如下圖:
兩句話總結(jié):
select和poll都需要輪詢每個(gè)文件描述符汗洒,epoll基于事件驅(qū)動(dòng),不用輪詢?
select和poll每次都需要拷貝文件描述符溢谤,epoll不用?
select最大連接數(shù)受限,epoll和poll最大連接數(shù)不受限
tips:epoll在內(nèi)核中的實(shí)現(xiàn)阀参,用紅黑樹管理事件塊
4.1通俗例子?
現(xiàn)在3y在公司里邊實(shí)習(xí)瞻坝,寫完的代碼需要給測(cè)試測(cè)一遍。?
select/poll情況:
開發(fā)在寫代碼所刀,此時(shí)測(cè)試挨個(gè)問所有開發(fā)者,你寫好程序了沒有忧吟?要測(cè)試嗎斩披?
epoll情況:
開發(fā)寫完代碼了讹俊,告訴測(cè)試:“我寫好代碼了煌抒,你去測(cè)測(cè)仍劈,功能是XXX”。于是測(cè)試高高興興去找bug了摧玫。
其他通俗描述[1]:
一個(gè)酒吧服務(wù)員(一個(gè)線程)耳奕,前面趴了一群醉漢,突然一個(gè)吼一聲“倒酒”(事件)屋群,你小跑過去給他倒一杯坏挠,然后隨他去吧,突然又一個(gè)要倒酒对竣,你又過去倒上榜配,就這樣一個(gè)服務(wù)員服務(wù)好多人,有時(shí)沒人喝酒蛋褥,服務(wù)員處于空閑狀態(tài)烙心,可以干點(diǎn)別的玩玩手機(jī)。至于epoll與select淫茵,poll的區(qū)別在于后兩者的場(chǎng)景中醉漢不說話,你要挨個(gè)問要不要酒铆铆,沒時(shí)間玩手機(jī)了丹喻。io多路復(fù)用大概就是指這幾個(gè)醉漢共用一個(gè)服務(wù)員。
來源:
www.zhihu.com/question/32…
其他通俗描述[2]:
簡(jiǎn)單舉個(gè)例子(可能也不是很形象)select/poll飯店服務(wù)員(內(nèi)核)告訴飯店老板(用戶程序):”現(xiàn)在有客人結(jié)賬“但是這個(gè)服務(wù)員沒人明確告訴老板,哪幾桌的客人結(jié)帳骑冗。老板得自兒一個(gè)一個(gè)桌子去問:請(qǐng)問是你要結(jié)帳?epoll飯店服務(wù)員(內(nèi)核)告訴飯店老板(用戶程序):”1,2,5號(hào)客人結(jié)賬“老板就可以直接去1,2,5號(hào)桌收錢了
來源:
www.zhihu.com/question/21…
深入了解參考資料:
www.cnblogs.com/Anker/p/326…—>select贼涩、poll、epoll之間的區(qū)別總結(jié)[整理]
五谤绳、Java內(nèi)存模型?
JVM博文回顧:
JVM如何從入門到放棄的袒哥?
之前在寫JVM的時(shí)候,還一度把JVM內(nèi)存結(jié)構(gòu)與Java內(nèi)存模型給搞混了~~~還好有熱心的網(wǎng)友給我指出來瞎抛。?
JVM內(nèi)存結(jié)構(gòu):
Java內(nèi)存模型:
操作變量時(shí)的規(guī)則:
Java內(nèi)存模型規(guī)定了所有的變量都存儲(chǔ)在主內(nèi)存?
線程的工作內(nèi)存中保存了被該線程使用到的變量的主內(nèi)存副本拷貝?
線程對(duì)變量的所有操作(讀取却紧、賦值等)都必須在工作內(nèi)存中進(jìn)行,而不能直接讀寫主內(nèi)存中的變量
從工作內(nèi)存同步回主內(nèi)存實(shí)現(xiàn)是通過以下的8種操作來完成:
lock(鎖定):作用于主內(nèi)存的變量断凶,把一個(gè)變量標(biāo)識(shí)為一條線程獨(dú)占狀態(tài)巫俺。?
unlock(解鎖):作用于主內(nèi)存變量,把一個(gè)處于鎖定狀態(tài)的變量釋放出來砚著,釋放后的變量才可以被其他線程鎖定痴昧。?
read(讀取):作用于主內(nèi)存變量舌镶,把一個(gè)變量值從主內(nèi)存?zhèn)鬏數(shù)骄€程的工作內(nèi)存中豪娜,以便隨后的load動(dòng)作使用?
load(載入):作用于工作內(nèi)存的變量,它把read操作從主內(nèi)存中得到的變量值放入工作內(nèi)存的變量副本中否灾。?
use(使用):作用于工作內(nèi)存的變量鸣奔,把工作內(nèi)存中的一個(gè)變量值傳遞給執(zhí)行引擎惩阶,每當(dāng)虛擬機(jī)遇到一個(gè)需要使用變量的值的字節(jié)碼指令時(shí)將會(huì)執(zhí)行這個(gè)操作扣汪。?
assign(賦值):作用于工作內(nèi)存的變量,它把一個(gè)從執(zhí)行引擎接收到的值賦值給工作內(nèi)存的變量冬筒,每當(dāng)虛擬機(jī)遇到一個(gè)給變量賦值的字節(jié)碼指令時(shí)執(zhí)行這個(gè)操作茅主。?
store(存儲(chǔ)):作用于工作內(nèi)存的變量,把工作內(nèi)存中的一個(gè)變量的值傳送到主內(nèi)存中匀奏,以便隨后的write的操作学搜。?
write(寫入):作用于主內(nèi)存的變量,它把store操作從工作內(nèi)存中一個(gè)變量的值傳送到主內(nèi)存的變量中聚磺。
Java內(nèi)存模型是圍繞著在并發(fā)過程中如何處理原子性炬丸、可見性和有序性這3個(gè)特征來建立的?
保證原子性的操作:
read、load焕阿、assign首启、use、store和write?
synchronized鎖
保證有序性(重排序?qū)е聼o序)的操作:
volatile?
synchronized鎖
保證可見性:
volatile?
synchronized鎖?
final
在上面也說了褒纲,有序性可以通過volatile和synchronized鎖來保證钥飞,但我們一般寫程序的時(shí)候不會(huì)總是關(guān)注代碼的有序性的。其實(shí)读宙,我們Java內(nèi)部中有一個(gè)原則,叫做先行發(fā)生原則(happens-before)
“先行發(fā)生”(happens-before)原則可以通過:幾條規(guī)則一攬子地解決并發(fā)環(huán)境下兩個(gè)操作之間是否可能存在沖突的所有問題?
有了這些規(guī)則唇兑,并且我們的操作是在這些規(guī)則定義的范圍之內(nèi)。我們就可以確保,A操作肯定比B操作先發(fā)生(不會(huì)出現(xiàn)重排序的問題)
“先行發(fā)生”(happens-before)原則有下面這么幾條:
程序次序規(guī)則(Program Order Rule):在一個(gè)線程內(nèi)帕棉,按照程序代碼順序饼记,書寫在前面的操作先行發(fā)生于書寫在后面的操作。準(zhǔn)確地說即纲,應(yīng)該是控制流順序而不是程序代碼順序博肋,因?yàn)橐紤]分支、循環(huán)等結(jié)構(gòu)匪凡。?
管程鎖定規(guī)則(Monitor Lock Rule):一個(gè)unlock操作先行發(fā)生于后面對(duì)同一個(gè)鎖的lock操作病游。這里必須強(qiáng)調(diào)的是同一個(gè)鎖,而“后面”是指時(shí)間上的先后順序衬衬。?
volatile變量規(guī)則(Volatile Variable Rule):對(duì)一個(gè)volatile變量的寫操作先行發(fā)生于后面對(duì)這個(gè)變量的讀操作滋尉,這里的“后面”同樣是指時(shí)間上的先后順序。線程啟動(dòng)規(guī)則(Thread Start Rule):Thread對(duì)象的start()方法先行發(fā)生于此線程的每一個(gè)動(dòng)作兼砖。?
線程終止規(guī)則(Thread Termination Rule):線程中的所有操作都先行發(fā)生于對(duì)此線程的終止檢測(cè)讽挟,我們可以通過Thread.join()方法結(jié)束、Thread.isAlive()的返回值等手段檢測(cè)到線程已經(jīng)終止執(zhí)行耽梅。?
線程中斷規(guī)則(Thread Interruption Rule):對(duì)線程interrupt()方法的調(diào)用先行發(fā)生于被中斷線程的代碼檢測(cè)到中斷事件的發(fā)生,可以通過Thread.interrupted()方法檢測(cè)到是否有中斷發(fā)生诅迷。?
對(duì)象終結(jié)規(guī)則(Finalizer Rule):一個(gè)對(duì)象的初始化完成(構(gòu)造函數(shù)執(zhí)行結(jié)束)先行發(fā)生于它的finalize()方法的開始。?
傳遞性(Transitivity):如果操作A先行發(fā)生于操作B趟畏,操作B先行發(fā)生于操作C滩租,那就可以得出操作A先行發(fā)生于操作C的結(jié)論。
參考資料:
【深入理解JVM】:Java內(nèi)存模型JMM:blog.csdn.net/u011080472/…?
Java的內(nèi)存模型(1):www.cnblogs.com/jian0110/p/…
六猎莲、最后?
本文簡(jiǎn)單整理了一下在學(xué)習(xí)中做的筆記技即,還有在網(wǎng)上遇到一些比較重要的知識(shí)點(diǎn)(面試題)~希望大家看完能有所收益。?