面试必备:synchronized的底层原理?
最近更新的XX必備系列適合直接背答案,不深究,不喜勿噴。
你能說簡單說一下synchronize嗎?
可別真簡單一句話就說完了呀~
參考回答:
synchronize是java中的關(guān)鍵字,可以用來修飾實(shí)例方法、靜態(tài)方法、還有代碼塊;主要有三種作用:可以確保原子性、可見性、有序性,原子性就是能夠保證同一時(shí)刻有且只有一個(gè)線程在操作共享數(shù)據(jù),其他線程必須等該線程處理完數(shù)據(jù)后才能進(jìn)行;可見性就是當(dāng)一個(gè)線程在修改共享數(shù)據(jù)時(shí),其他線程能夠看到,保證可見性,volatile關(guān)鍵字也有這個(gè)功能;有序性就是,被synchronize鎖住后的線程相當(dāng)于單線程,在單線程環(huán)境jvm的重排序是不會(huì)改變程序運(yùn)行結(jié)果的,可以防止重排序?qū)Χ嗑€程的影響。
補(bǔ)充:我們來看一下上邊這個(gè)回答,其中有好幾個(gè)部分可以繼續(xù)延伸,這里指的延伸就是面試官可以再繼續(xù)問你的問題。
延伸一:java內(nèi)存模型的三大特性,或者是說一下java內(nèi)存模型,或者是synchronize跟java內(nèi)存模型有什么關(guān)系嗎?
首先補(bǔ)充為何會(huì)問到j(luò)ava內(nèi)存模型,因?yàn)镾ynchronize的三種作用其實(shí)就是java內(nèi)存模型保證的,再就是這個(gè)問題可能單獨(dú)就蹦到考察java內(nèi)存模型(JMM)上了。
1、什么是java內(nèi)存模型:java虛擬機(jī)規(guī)范中定義了java內(nèi)存模型是用來屏蔽各種硬件和操作系統(tǒng)間內(nèi)存的差異,來實(shí)現(xiàn)java程序在各平臺(tái)下并發(fā)一致性,再就是,java內(nèi)存模型并不是真實(shí)存在的,他只是一種抽象概念,定義了線程和主內(nèi)存之間的抽象關(guān)系,也就是線程之間的共享變量存儲(chǔ)在主內(nèi)存中,每個(gè)線程都有一個(gè)私有的本地內(nèi)存,本地內(nèi)存存儲(chǔ)了該線程共享變量的副本。
2、java內(nèi)存模型的三大特性:java內(nèi)存模型有三大特性,原子性、可見性、有序性。
原子性:要么執(zhí)行,要么不執(zhí)行,主要使用互斥鎖Synchronize或者lock來保證操作的原子性;
可見性:在變量修改后將新值同步回主內(nèi)存,主要有兩種實(shí)現(xiàn)方式,一是volatile,被volatile修飾的變量發(fā)生修改后會(huì)立即刷新到主內(nèi)存;二是使用Synchronize或者lock,當(dāng)一個(gè)變量unlock之前會(huì)將變量的修改刷新到主內(nèi)存中;
有序性:在Java內(nèi)存模型中,允許編譯器和處理器對(duì)指令進(jìn)行重排序,但是重排序不會(huì)影響單線程的執(zhí)行結(jié)果,卻會(huì)影響多線程并發(fā)執(zhí)行的正確性。主要有兩種方式確保有序性:volatile 和 Synchronize 關(guān)鍵字,volatile是通過添加內(nèi)存屏障的方式來禁止指令重排序,也就是重排序是不能把后面的指令放到內(nèi)存屏障之前執(zhí)行;Synchronize是保證同一時(shí)刻有且只有一個(gè)線程執(zhí)行同步代碼,類似于串聯(lián)順序執(zhí)行代碼。
延伸二:你了解先行發(fā)生原則(happens-before)嗎?
為什么會(huì)出現(xiàn)先行發(fā)生原則:從上邊我們也能看到,如果java內(nèi)存模型中所有的有序性都要靠volatile和Synchronize來實(shí)現(xiàn)的話,那么是非常繁瑣的,所以j就出現(xiàn)這么一個(gè)《先行發(fā)生原則》,用來判斷數(shù)據(jù)是否存在競爭、線程是否安全的重要依據(jù)。
參考回答:
先行發(fā)生原則是java內(nèi)存模型用來定義兩個(gè)操作之間的偏序關(guān)系。比如說A操作先發(fā)生于B操作,那么在B操作發(fā)生之前,A操作修改了內(nèi)存中的共享變量,那么就會(huì)被B操作察覺到。
先行發(fā)生原則其中包含8種規(guī)則,比如:程序員次序規(guī)則,volatile變量規(guī)則,線程的啟動(dòng)、中止、中斷等規(guī)則。
如果再問到能簡單介紹一下你說的這幾個(gè)規(guī)則嗎?一般不會(huì)問這么細(xì)的,了解即可。
程序員次序規(guī)則:在一個(gè)線程內(nèi),在程序前面的操作先行發(fā)生于后面的操作。
volatile變量規(guī)則:對(duì)一個(gè) volatile 變量的寫操作先行發(fā)生于后面對(duì)這個(gè)變量的讀操作。
線程啟動(dòng)規(guī)則:Thread 對(duì)象的 start() 方法調(diào)用先行發(fā)生于此線程的每一個(gè)動(dòng)作。
線程加入規(guī)則:Thread 對(duì)象的結(jié)束先行發(fā)生于 join() 方法返回。
線程中斷規(guī)則:對(duì)線程 interrupt() 方法的調(diào)用先行發(fā)生于被中斷線程的代碼檢測到中斷事件的發(fā)生,可以通過 interrupted() 方法檢測到是否有中斷發(fā)生。
延伸三:volatile的作用,volatile跟Synchronize的區(qū)別
補(bǔ)充:如果上邊不答到volatile,可能會(huì)跳過這個(gè)問題,但是多吹點(diǎn)說不定工資高點(diǎn)。
參考回答:
volatile的作用:volatile關(guān)鍵字主要作用是確??梢娦愿行蛐?#xff0c;當(dāng)一個(gè)共享變量被volatile修飾,如果一個(gè)線程修改了這個(gè)共享變量,那么其他線程就會(huì)立馬可知,強(qiáng)制刷新到主內(nèi)存。
volatile跟Synchronize的區(qū)別:
延伸四:你能說說你剛剛提到的重排序嗎?
你是否想過,重排序?yàn)槭裁磿?huì)對(duì)多線程產(chǎn)生影響?
參考回答:
重排序是編譯器和處理器為了優(yōu)化程序性能而對(duì)指令進(jìn)行重新排序的一種手段。重排序可以保證最終執(zhí)行的結(jié)果是與程序順序執(zhí)行的結(jié)果一致,并且只會(huì)對(duì)不存在數(shù)據(jù)依賴性的指令進(jìn)行重排序,重排序在單線程下對(duì)最終執(zhí)行結(jié)果是沒有影響的,但是在多線程下就會(huì)存在問題。
舉個(gè)例子:
int?a?=?1;int?b?=?2;
int?c?=?a*b;
如上,a與c之間存在數(shù)據(jù)依賴關(guān)系,所以c不能排到A的前面,同時(shí)b與c之間也存在數(shù)據(jù)依賴關(guān)系,所以,c也不能排到B的前面,但是a與b之間是不存在數(shù)據(jù)依賴關(guān)系的,所以a與b之間是可以進(jìn)行重排序的,但是無論怎么重排序都是不會(huì)影響到c的值。
但是在多線程中就不一樣了,如下代碼:
class?Test{????/**?我是變量a?**/
????int?a?=?0;
????/**?我是用來標(biāo)記變量a是否被寫入?**/
????boolean?flag?=?false;
????/**?我是寫操作?**/
????public?void?writer(){
????????a?=?1;?/**?第1步?**/
????????flag?=?true;?/**?第2步?**/
????}
????/**?我是讀操作?**/
????public?void?reader(){
????????if(flag){???????????/**?第3步?**/
????????????int?i?=?a?*?a;??/**?第4步?**/
?????????????......
????????}
????}?
}
flag是一個(gè)變量,用來表示變量a是否已被寫入。這里假設(shè)有兩個(gè)線程A和B ,A線程首先執(zhí)行writer()方法,隨后線程B執(zhí)行reader()方法。線程B在執(zhí)行操作第4步的時(shí)候,能否看到線程A在操作共享變量a的寫入呢?
答案是:在多線程的情況下,不一定能看到;
由于操作1和操作2沒有數(shù)據(jù)依賴關(guān)系,編譯器和處理器可以對(duì)這兩個(gè)操作重排序;同樣,操作3和操作4沒有數(shù)據(jù)依賴關(guān)系,編譯器和處理器也可以對(duì)這兩個(gè)操作重排序。
具體細(xì)節(jié)可以看一下這篇文章,了解一下重排序?qū)Χ嗑€程的影響:https://blog.csdn.net/zhushuai1221/article/details/51491578
你能說一下Synchronize底層原理嗎?
參考回答:
synchronized的底層原理是跟monitor有關(guān),也就是視圖器鎖,每個(gè)對(duì)象都有一個(gè)關(guān)聯(lián)的monitor,當(dāng)Synchronize獲得monitor對(duì)象的所有權(quán)后會(huì)進(jìn)行兩個(gè)指令:加鎖指令monitorenter跟減鎖指令monitorexit。
monitor里面有個(gè)計(jì)數(shù)器,初始值是從0開始的。如果一個(gè)線程想要獲取monitor的所有權(quán),就看看它的計(jì)數(shù)器是不是0,如果是0的話,那么就說明沒人獲取鎖,那么它就可以獲取鎖了,然后將計(jì)數(shù)器+1,也就是執(zhí)行monitorenter加鎖指令;monitorexit減鎖指令是跟在程序執(zhí)行結(jié)束和異常里的,如果不是0的話,就會(huì)陷入一個(gè)堵塞等待的過程,直到為0等待結(jié)束。
最后
博客地址:https://www.cgblog.com/niceyoo
如果覺得這篇文章有丶東西,不放關(guān)注一下我,關(guān)注是對(duì)我最大的鼓勵(lì)~
18年??飘厴I(yè)后,期間一度迷茫,最近我創(chuàng)建了一個(gè)公眾號(hào)用來記錄自己的成長。
總結(jié)
以上是生活随笔為你收集整理的面试必备:synchronized的底层原理?的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 制作.rpgsave存档修改器
- 下一篇: 气象数据集