1嫁佳、synchronized修飾普通方法和靜態(tài)方法的區(qū)別?什么是可見性谷暮?
對(duì)象鎖是用于對(duì)象實(shí)例方法蒿往,或者一個(gè)對(duì)象實(shí)例上的, 類鎖 是用于類的靜態(tài)方法或者一個(gè)類的class對(duì)象上的湿弦。我們知道瓤漏,類的對(duì)象實(shí)例可以有很多個(gè),但是每個(gè)類只有一個(gè)class對(duì)象颊埃,所以不同對(duì)象實(shí)例的對(duì)象鎖是互不干擾的蔬充,但是每個(gè)類只有一個(gè)類鎖。
但是有一點(diǎn)必須注意的是班利,其實(shí)類鎖只是一個(gè)概念上的東西饥漫,并不是真實(shí)存在的,類鎖其實(shí)鎖的是每個(gè)類的對(duì)應(yīng)的class對(duì)象罗标。類鎖和對(duì)象鎖之間也是互不干擾的庸队。
對(duì)象鎖:
/**
* 對(duì)象鎖 synchronized修飾普通方法 鎖的是當(dāng)前類實(shí)例對(duì)象
*/
public synchronized void syncMethod1() {
//do working...
}
復(fù)制代碼
private final Object o = new Object();
/**
* 對(duì)象鎖 鎖的是Object對(duì)象實(shí)例
*/
public void syncMethod2() {
synchronized (o) {
//do working...
}
}
復(fù)制代碼
類鎖:
/**
* 類鎖 synchronized修飾靜態(tài)方法 鎖的是當(dāng)前類唯一的.class對(duì)象,也就是SynchronizedType.class馒稍,等同于下面的syncMethod3()
*/
public static synchronized void syncMethod3() {
//do working...
}
復(fù)制代碼
/**
* 類鎖 鎖的是當(dāng)前類唯一的.class對(duì)象皿哨。
*/
public void syncMethod4() {
synchronized (SynchronizedType.class) {
//do working...
}
}
復(fù)制代碼
可見性是指當(dāng)多個(gè)線程訪問同一個(gè)變量時(shí)浅侨,一個(gè)線程修改了這個(gè)變量的值纽谒,其他線程能夠立即看得到修改的值。
由于線程對(duì)變量的所有操作都必須在工作內(nèi)存中進(jìn)行如输,而不能直接讀寫主內(nèi)存中的變量鼓黔,那么對(duì)于共享變量V,它們首先是在自己的工作內(nèi)存不见,之后再同步到主內(nèi)存澳化。可是并不會(huì)及時(shí)的刷到主存中稳吮,而是會(huì)有一定時(shí)間差缎谷。很明顯,這個(gè)時(shí)候線程A對(duì)變量V的操作對(duì)于線程B而言就不具備可見性了 灶似。
要解決共享對(duì)象可見性這個(gè)問題列林,我們可以使用 volatile 關(guān)鍵字或者是加鎖瑞你。
2、鎖分哪幾類希痴?
3者甲、CAS無鎖編程的原理。
使用當(dāng)前的處理器基本都支持CAS()的指令,只不過每個(gè)廠家所實(shí)現(xiàn)的算法并不一樣,每一個(gè)CAS操作過程都包含三個(gè)運(yùn)算符:一個(gè)內(nèi)存地址V捂蕴,一個(gè)期望的值A(chǔ)和一個(gè)新值B悔捶,操作的時(shí)候如果這個(gè)地址上存放的值等于這個(gè)期望的值A(chǔ),則將地址上的值賦為新值B勺爱,否則不做任何操作。
CAS的基本思路就是,如果這個(gè)地址上的值和期望的值相等扫倡,則給其賦予新值,否則不做任何事兒竟纳,但是要返回原值是多少撵溃。循環(huán)CAS就是在一個(gè)循環(huán)里不斷的做CAS操作,直到成功為止锥累。
還可以說說CAS的三大問題缘挑。
4、ReentrantLock的實(shí)現(xiàn)原理桶略。
線程可以重復(fù)進(jìn)入任何一個(gè)它已經(jīng)擁有的鎖所同步著的代碼塊语淘, synchronized 、 ReentrantLock 都是可重入的鎖际歼。在實(shí)現(xiàn)上惶翻,就是線程每次獲取鎖時(shí)判定如果獲得鎖的線程是它自己時(shí),簡單將計(jì)數(shù)器累積即可鹅心,每 釋放一次鎖吕粗,進(jìn)行計(jì)數(shù)器累減,直到計(jì)算器歸零旭愧,表示線程已經(jīng)徹底釋放鎖颅筋。
底層則是利用了JUC中的AQS來實(shí)現(xiàn)的。
5输枯、AQS原理议泵。
是用來構(gòu)建鎖或者其他同步組件的基礎(chǔ)框架,比如 ReentrantLock 桃熄、 ReentrantReadWriteLock 和 CountDownLatch 就是基于AQS實(shí)現(xiàn)的先口。它使用了一個(gè)int成員變量表示同步狀態(tài),通過內(nèi)置的FIFO隊(duì)列來完成資源獲取線程的排隊(duì)工作。它是CLH隊(duì)列鎖的一種變體實(shí)現(xiàn)碉京。它可以實(shí)現(xiàn)2種同步方式:獨(dú)占式桩引,共享式。
AQS的主要使用方式是繼承收夸,子類通過繼承AQS并實(shí)現(xiàn)它的抽象方法來管理同步狀態(tài)坑匠,同步器的設(shè)計(jì)基于模板方法模式,所以如果要實(shí)現(xiàn)我們自己的同步工具類就需要覆蓋其中幾個(gè)可重寫的方法卧惜,如 tryAcquire 厘灼、 tryReleaseShared 等等。
這樣設(shè)計(jì)的目的是同步組件(比如鎖)是面向使用者的咽瓷,它定義了使用者與同步組件交互的接口(比如可以允許兩個(gè)線程并行訪問)设凹,隱藏了實(shí)現(xiàn)細(xì)節(jié);同步器面向的是鎖的實(shí)現(xiàn)者茅姜,它簡化了鎖的實(shí)現(xiàn)方式闪朱,屏蔽了同步狀態(tài)管理、線程的排隊(duì)钻洒、等待與喚醒等底層操作奋姿。這樣就很好地隔離了使用者和實(shí)現(xiàn)者所需關(guān)注的領(lǐng)域。
在內(nèi)部素标,AQS維護(hù)一個(gè)共享資源state称诗,通過內(nèi)置的FIFO來完成獲取資源線程的排隊(duì)工作。該隊(duì)列由一個(gè)一個(gè)的Node結(jié)點(diǎn)組成头遭,每個(gè)Node結(jié)點(diǎn)維護(hù)一個(gè)prev引用和next引用寓免,分別指向自己的前驅(qū)和后繼結(jié)點(diǎn),構(gòu)成一個(gè)雙端雙向鏈表计维。
6袜香、synchronized的原理以及與ReentrantLock的區(qū)別。
synchronized (this) 原理:涉及兩條指令: monitorenter 鲫惶, monitorexit 蜈首;再說同步方法,從同步方法反編譯的結(jié)果來看剑按,方法的同步并沒有通過指令 monitorenter 和 monitorexit 來實(shí)現(xiàn)疾就,相對(duì)于普通方法澜术,其常量池中多了 ACC_SYNCHRONIZED 標(biāo)示符艺蝴。
JVM就是根據(jù)該標(biāo)示符來實(shí)現(xiàn)方法的同步的:當(dāng)方法被調(diào)用時(shí),調(diào)用指令將會(huì)檢查方法的 ACC_SYNCHRONIZED 訪問標(biāo)志是否被設(shè)置鸟废,如果設(shè)置了猜敢,執(zhí)行線程將先獲取monitor,獲取成功之后才能執(zhí)行方法體,方法執(zhí)行完后再釋放monitor缩擂。在方法執(zhí)行期間鼠冕,其他任何線程都無法再獲得同一個(gè)monitor對(duì)象。
7胯盯、synchronized鎖了那些優(yōu)化懈费?
引入如自旋鎖、適應(yīng)性自旋鎖博脑、鎖消除憎乙、鎖粗化、偏向鎖叉趣、輕量級(jí)鎖泞边、逃逸分析等技術(shù)來減少鎖操作的開銷。
逃逸分析
如果證明一個(gè)對(duì)象不會(huì)逃逸方法外或者線程外疗杉,則可針對(duì)此變量進(jìn)行優(yōu)化:
同步消除 synchronization Elimination 阵谚,如果一個(gè)對(duì)象不會(huì)逃逸出線程,則對(duì)此變量的同步措施可消除烟具。
鎖消除和粗化
鎖消除:虛擬機(jī)的運(yùn)行時(shí)編譯器在運(yùn)行時(shí)如果檢測到一些要求同步的代碼上不可能發(fā)生共享數(shù)據(jù)競爭梢什,則會(huì)去掉這些鎖。
鎖粗化:將臨近的代碼塊用同一個(gè)鎖合并起來朝聋。消除無意義的鎖獲取和釋放绳矩,可以提高程序運(yùn)行性能。
8玖翅、synchronized static與非static鎖的區(qū)別和范圍翼馆。
對(duì)象鎖是用于對(duì)象實(shí)例方法,或者一個(gè)對(duì)象實(shí)例上的金度,類鎖是用于類的靜態(tài)方法或者一個(gè)類的class對(duì)象上的应媚。我們知道,類的對(duì)象實(shí)例可以有很多個(gè)猜极,但是每個(gè)類只有一個(gè)class對(duì)象中姜,所以不同對(duì)象實(shí)例的對(duì)象鎖是互不干擾的,但是每個(gè)類只有一個(gè)類鎖跟伏。
但是有一點(diǎn)必須注意的是丢胚,其實(shí)類鎖只是一個(gè)概念上的東西,并不是真實(shí)存在的受扳,類鎖其實(shí)鎖的是每個(gè)類的對(duì)應(yīng)的class對(duì)象携龟。類鎖和對(duì)象鎖之間也是互不干擾的。
9勘高、volatile 能否保證線程安全峡蟋?在DCL上的作用是什么坟桅?
不能保證,在DCL的作用是: volatile 是會(huì)保證被修飾的變量的可見性和 有序性蕊蝗,保證了單例模式下仅乓,保證在創(chuàng)建對(duì)象的時(shí)候的執(zhí)行順序一定是
1.分配內(nèi)存空間
2.實(shí)例化對(duì)象instance
3.把instance引用指向已分配的內(nèi)存空間,此時(shí)instance有了內(nèi)存地址,不再為null了的步驟, 從而保證了instance要么為null 要么是已經(jīng)完全初始化好的對(duì)象。
10蓬戚、volatile和synchronize有什么區(qū)別夸楣?
volatile 是最輕量的同步機(jī)制。
volatile 保證了不同線程對(duì)這個(gè)變量進(jìn)行操作時(shí)的可見性子漩,即一個(gè)線程修改了某個(gè)變量的值裕偿,這新值對(duì)其他線程來說是立即可見的。但是 volatile 不能保證操作的原子性痛单,因此多線程下的寫復(fù)合操作會(huì)導(dǎo)致線程安全問題嘿棘。
關(guān)鍵字 synchronized 可以修飾方法或者以同步塊的形式來進(jìn)行使用,它主要確保多個(gè)線程在同一個(gè)時(shí)刻旭绒,只能有一個(gè)線程處于方法或者同步塊中鸟妙,它保證了線程對(duì)變量訪問的可見性和排他性,又稱為內(nèi)置鎖機(jī)制挥吵。
11重父、什么是守護(hù)線程?你是如何退出一個(gè)線程的忽匈?
Daemon(守護(hù))線程是一種支持型線程房午,因?yàn)樗饕挥米鞒绦蛑泻笈_(tái)調(diào)度以及支持性工作。這意味著丹允,當(dāng)一個(gè)Java虛擬機(jī)中不存在非Daemon線程的時(shí)候郭厌,Java虛擬機(jī)將會(huì)退出〉癖危可以通過調(diào)用 Thread.setDaemon(true) 將線程設(shè)置為Daemon線程折柠。我們一般用不上,比如垃圾回收線程就是Daemon線程批狐。
線程的中止:
要么是 run() 執(zhí)行完成了扇售,要么是拋出了一個(gè)未處理的異常導(dǎo)致線程提前結(jié)束。
暫停嚣艇、恢復(fù)和停止操作對(duì)應(yīng)在線程 Thread 的API就是 suspend() 承冰、 resume() 和 stop() 。但是這些API是過期的食零,也就是不建議使用的困乒。因?yàn)闀?huì)導(dǎo)致程序可能工作在不確定狀態(tài)下。
安全的中止則是其他線程通過調(diào)用某個(gè)線程A的 interrupt() 方法對(duì)其進(jìn)行中斷操作慌洪,被中斷的線程則是通過線程通過方法 isInterrupted() 來進(jìn)行判斷是否被中斷顶燕,也可以調(diào)用靜態(tài)方法 Thread.interrupted() 來進(jìn)行判斷當(dāng)前線程是否被中斷,不過 Thread.interrupted() 會(huì)同時(shí)將中斷標(biāo)識(shí)位改寫為false冈爹。
12涌攻、sleep 、wait频伤、yield 的區(qū)別恳谎,wait 的線程如何喚醒它?
yield() 方法:使當(dāng)前線程讓出CPU占有權(quán)憋肖,但讓出的時(shí)間是不可設(shè)定的因痛。也不會(huì)釋放鎖資源。所有執(zhí)行yield()的線程有可能在進(jìn)入到就緒狀態(tài)后會(huì)被操作系統(tǒng)再次選中馬上又被執(zhí)行岸更。
yield() 鸵膏、 sleep() 被調(diào)用后,都不會(huì)釋放當(dāng)前線程所持有的鎖怎炊。
調(diào)用 wait() 方法后谭企,會(huì)釋放當(dāng)前線程持有的鎖,而且當(dāng)前被喚醒后评肆,會(huì)重新去競爭鎖债查,鎖競爭到后才會(huì)執(zhí)行 wait() 方法后面的代碼。
wait通常被用于線程間交互瓜挽, sleep() 通常被用于暫停執(zhí)行盹廷, yield() 方法使當(dāng)前線程讓出CPU占有權(quán)。wait的線程使用 notify/notifyAll() 進(jìn)行喚醒久橙。
13俄占、sleep是可中斷的么?
sleep 本身就支持中斷淆衷,如果線程在 sleep 期間被中斷颠放,則會(huì)拋出一個(gè)中斷異常。
14吭敢、線程生命周期碰凶。
Java中線程的狀態(tài)分為6種:
- 初始(NEW):新創(chuàng)建了一個(gè)線程對(duì)象,但還沒有調(diào)用start()方法鹿驼。
- 運(yùn)行(RUNNABLE):Java線程中將就緒(ready)和運(yùn)行中(running)兩種狀態(tài)籠統(tǒng)的稱為“運(yùn)行”欲低。
線程對(duì)象創(chuàng)建后,其他線程(比如main線程)調(diào)用了該對(duì)象的start()方法畜晰。該狀態(tài)的線程位于可運(yùn)行線程池中砾莱,等待被線程調(diào)度選中,獲取CPU的使用權(quán)凄鼻,此時(shí)處于就緒狀態(tài)(ready)腊瑟。就緒狀態(tài)的線程在獲得CPU時(shí)間片后變?yōu)檫\(yùn)行中狀態(tài)(running)聚假。
3. 阻塞(BLOCKED):表示線程阻塞于鎖。
4. 等待(WAITING):進(jìn)入該狀態(tài)的線程需要等待其他線程做出一些特定動(dòng)作(通知或中斷)闰非。
5. 超時(shí)等待(TIMED_WAITING):該狀態(tài)不同于WAITING膘格,它可以在指定的時(shí)間后自行返回。
- 終止(TERMINATED):表示該線程已經(jīng)執(zhí)行完畢财松。
15瘪贱、ThreadLocal是什么?
ThreadLocal 是Java里一種特殊的變量辆毡。 ThreadLocal 為每個(gè)線程都提供了變量的副本菜秦,使得每個(gè)線程在某一時(shí)間訪問到的并非同一個(gè)對(duì)象,這樣就隔離了多個(gè)線程對(duì)數(shù)據(jù)的數(shù)據(jù)共享舶掖。
在內(nèi)部實(shí)現(xiàn)上球昨,每個(gè)線程內(nèi)部都有一個(gè) ThreadLocalMap ,用來保存每個(gè)線程所擁有的變量副本眨攘。
16褪尝、線程池基本原理。
在開發(fā)過程中期犬,合理地使用線程池能夠帶來3個(gè)好處河哑。
第一:降低資源消耗。
第二:提高響應(yīng)速度龟虎。
第三:提高線程的可管理性璃谨。
1)如果當(dāng)前運(yùn)行的線程少于 corePoolSize ,則創(chuàng)建新線程來執(zhí)行任務(wù)(注意鲤妥,執(zhí)行這一步驟需要獲取全局鎖)佳吞。
2)如果運(yùn)行的線程等于或多于 corePoolSize ,則將任務(wù)加入 BlockingQueue 棉安。
3)如果無法將任務(wù)加入 BlockingQueue (隊(duì)列已滿)底扳,則創(chuàng)建新的線程來處理任務(wù)。
4)如果創(chuàng)建新線程將使當(dāng)前運(yùn)行的線程超出 maximumPoolSize 贡耽,任務(wù)將被拒絕衷模,并調(diào)用 RejectedExecutionHandler.rejectedExecution() 方法。
17蒲赂、有三個(gè)線程T1阱冶,T2,T3滥嘴,怎么確保它們按順序執(zhí)行木蹬?
使用join()實(shí)現(xiàn)。