Java高并發(fā)綜合

這篇文章是研一剛?cè)雽W(xué)時(shí)寫(xiě)的,今天整理草稿時(shí)才被我挖出來(lái)。當(dāng)時(shí)混混沌沌的面試,記下來(lái)了一些并發(fā)的面試問(wèn)題,很多還沒(méi)有回答。到現(xiàn)在也學(xué)習(xí)了不少并發(fā)的知識(shí),回過(guò)頭來(lái)看這些問(wèn)題和當(dāng)時(shí)整理的答案,漏洞百出又十分可笑。發(fā)表出來(lái)權(quán)當(dāng)對(duì)自己的一個(gè)提醒——如果不能一直進(jìn)步,你就看不到當(dāng)初傻逼的自己。


曾經(jīng),我在面試Java研發(fā)實(shí)習(xí)生時(shí)最常聽(tīng)到的一句話就是:

搞Java怎么能不學(xué)并發(fā)呢?

沒(méi)錯(cuò),真的是經(jīng)過(guò)了面試官的無(wú)數(shù)鄙視,我才知道Java并發(fā)編程在Java語(yǔ)言中的重要性。

并發(fā)模型

悲觀鎖和樂(lè)觀鎖的理解及如何實(shí)現(xiàn),有哪些實(shí)現(xiàn)方式?

悲觀鎖

悲觀鎖假設(shè)最壞的情況(如果你不鎖門(mén),那么搗蛋鬼就會(huì)闖入并搞得一團(tuán)糟),并且只有在確保其他線程不會(huì)干擾(通過(guò)獲取正確的鎖)的情況下才能執(zhí)行下去。

常見(jiàn)實(shí)現(xiàn)如獨(dú)占鎖等。

安全性更高,但在中低并發(fā)程度下的效率更低。

樂(lè)觀鎖

樂(lè)觀鎖借助沖突檢查機(jī)制來(lái)判斷在更新過(guò)程中是否存在其他線程的干擾,如果存在,這個(gè)操作將失敗,并且可以重試(也可以不重試)。

常見(jiàn)實(shí)現(xiàn)如CAS等。

部分樂(lè)觀鎖削弱了一致性,但中低并發(fā)程度下的效率大大提高。

并發(fā)編程

Java中如何創(chuàng)建一個(gè)線程

從面相接口的角度上講,實(shí)際上只有一種方法實(shí)現(xiàn)Runable接口;但Thread類(lèi)為線程操作提供了更多的支持,所以通常做法是實(shí)現(xiàn)Runable接口,實(shí)例化并傳入Thread類(lèi)的構(gòu)造函數(shù)。

  1. 繼承Thread,覆寫(xiě)run方法
  2. 實(shí)現(xiàn)Runable接口,覆寫(xiě)run方法

Vector(HashTable)如何實(shí)現(xiàn)線程安全

通過(guò)synchronized關(guān)鍵字修飾每個(gè)方法。

依據(jù)synchronized關(guān)鍵字引申出以下問(wèn)題。

synchronized修飾方法和修飾代碼塊時(shí)有何不同

持有鎖的對(duì)象不同:

  1. 修飾方法時(shí):this引用的當(dāng)前實(shí)例持有鎖
  2. 修飾代碼塊時(shí):要指定一個(gè)對(duì)象,該對(duì)象持有鎖

從而導(dǎo)致二者的意義不同:

  1. 同步代碼塊在鎖定的范圍上可能比同步方法要小,一般來(lái)說(shuō)鎖的范圍大小和性能是成反比的。
  2. 修飾代碼塊可以選擇對(duì)哪個(gè)對(duì)象加鎖,但是修飾方法只能給this對(duì)象加鎖。

ConcurrentHashMap的如何實(shí)現(xiàn)線程安全

ConcurrentHashMap的線程安全實(shí)現(xiàn)與HashTable不同:

  1. 可以將ConcurrentHashMap理解為,不直接持有一個(gè)HashMao,而是用多個(gè)Segment代替了一個(gè)HashMap。但實(shí)際實(shí)現(xiàn)的Map部分和HashMap的原理基本相同,對(duì)腳標(biāo)取模來(lái)確定table[i]所屬段,從而對(duì)不同的段獲取不同的段鎖。
  2. 每個(gè)Segment持有一個(gè)鎖,通過(guò)分段加鎖的方式,既實(shí)現(xiàn)了線程安全,又兼顧了性能

Java中有哪些實(shí)現(xiàn)并發(fā)編程的方法

要從最簡(jiǎn)單的答起,業(yè)界最常用的是重點(diǎn),有新意就放在最后。

  1. synchronized關(guān)鍵字
  2. 使用繼承自O(shè)bject類(lèi)的wait、notify、notifyAll方法
  3. 使用線程安全的API和集合類(lèi):
    1. 使用Vector、HashTable等線程安全的集合類(lèi)
    2. 使用Concurrent包中提供的ConcurrentHashMap、CopyOnWriteArrayList、ConcurrentLinkedQueue等弱一致性的集合類(lèi)
    3. 在Collections類(lèi)中有多個(gè)靜態(tài)方法,它們可以獲取通過(guò)同步方法封裝非同步集合而得到的集合,如List list = Collection.synchronizedList(new ArrayList())。
    4. 使用原子變量、volatile變量等
  4. 使用Concurrent包中提供的信號(hào)量Semaphore、閉鎖Latch、柵欄Barrier、交換器Exchanger、Callable&Future、阻塞隊(duì)列BlockingQueue等.
  5. 手動(dòng)使用Lock實(shí)現(xiàn)基于鎖的并發(fā)控制
  6. 手動(dòng)使用Condition或AQS實(shí)現(xiàn)基于條件隊(duì)列的并發(fā)控制
  7. 使用CAS和SPIN等實(shí)現(xiàn)非阻塞的并發(fā)控制
  8. 使用不變類(lèi)
  9. 其他并發(fā)模型還沒(méi)有涉及

從而引申出如下問(wèn)題:

ConcurrentHashMap的的實(shí)現(xiàn)原理(見(jiàn)前)

CopyOnWriteArrayList的復(fù)制操作發(fā)生在什么時(shí)機(jī)

synchronizedList&Vector的區(qū)別

  1. synchronizedList的實(shí)現(xiàn)中,synchronized關(guān)鍵字修飾代碼塊;Vector的實(shí)現(xiàn)中修飾方法。
  2. synchronizedList只封裝了add、get、remove等代碼塊,但I(xiàn)terator卻不是同步的,進(jìn)行遍歷時(shí)要手動(dòng)進(jìn)行同步處理;Vector中對(duì)Iterator也進(jìn)行了加鎖。
  3. synchronizedList能夠?qū)⑺蠰ist實(shí)現(xiàn)類(lèi)封裝為同步集合,其內(nèi)部持有的仍然是List的實(shí)現(xiàn)類(lèi)(ArrayList/LinkedList),所以除同步外,幾乎只有該實(shí)現(xiàn)類(lèi)和Vector的區(qū)別。

synchronized修飾方法和修飾代碼塊時(shí)有何不同(見(jiàn)前)

信號(hào)量Semaphore、閉鎖Latch、柵欄Barrier、交換器

Exchanger、Callable&Future、阻塞隊(duì)列BlockingQueue的實(shí)現(xiàn)原理

ConcurrentLinkedQueue的插入算法

算法核心可概括為兩步:

  1. 先檢測(cè)是否是中間狀態(tài)(SPIN)
  2. 再嘗試CAS插入

詳細(xì)待補(bǔ)充。

參考:

在java中wait和sleep方法的不同?

最大的不同是在等待時(shí)wait會(huì)釋放鎖,而sleep一直持有鎖。Wait通常被用于線程間交互,sleep通常被用于暫停執(zhí)行。

為什么wait, notify 和 notifyAll這些方法不在thread類(lèi)里面?

主要原因是JAVA提供的鎖是對(duì)象級(jí)的而不是線程級(jí)的,每個(gè)對(duì)象都有鎖,通過(guò)線程獲得。由于wait,notify和notifyAll都是鎖級(jí)別的操作,所以把他們定義在Object類(lèi)中因?yàn)殒i屬于對(duì)象。

為什么wait和notify方法要在同步塊中調(diào)用?

Java API強(qiáng)制要求這樣做,如果你不這么做,你的代碼會(huì)拋出IllegalMonitorStateException異常。還有一個(gè)原因是為了避免wait和notify之間產(chǎn)生競(jìng)態(tài)條件。

為什么你應(yīng)該在循環(huán)中檢查等待條件?

處于等待狀態(tài)的線程可能會(huì)收到錯(cuò)誤警報(bào)和偽喚醒,如果不在循環(huán)中檢查等待條件,程序就會(huì)在沒(méi)有滿足結(jié)束條件的情況下退出。

Java線程池中submit() 和 execute()方法有什么區(qū)別?

兩個(gè)方法都可以向線程池提交任務(wù),execute()方法的返回類(lèi)型是void,它定義在Executor接口中, 而submit()方法可以返回持有計(jì)算結(jié)果的Future對(duì)象,它定義在ExecutorService接口中,它擴(kuò)展了Executor接口,其它線程池類(lèi)像ThreadPoolExecutor和ScheduledThreadPoolExecutor都有這些方法。

volatile 變量和 atomic 變量有什么不同?

Volatile變量可以確保先行關(guān)系,即寫(xiě)操作會(huì)發(fā)生在后續(xù)的讀操作之前, 但它并不能保證原子性。例如用volatile修飾count變量那么 count++ 操作就不是原子性的。而AtomicInteger類(lèi)提供的atomic方法可以讓這種操作具有原子性如getAndIncrement()方法會(huì)原子性的進(jìn)行增量操作把當(dāng)前值加一,其它數(shù)據(jù)類(lèi)型和引用變量也可以進(jìn)行相似操作。

為什么Thread類(lèi)的sleep()和yield ()方法是靜態(tài)的?

Thread類(lèi)的sleep()和yield()方法將在當(dāng)前正在執(zhí)行的線程上運(yùn)行。所以在其他處于等待狀態(tài)的線程上調(diào)用這些方法是沒(méi)有意義的。這就是為什么這些方法是靜態(tài)的。它們可以在當(dāng)前正在執(zhí)行的線程中工作,并避免程序員錯(cuò)誤的認(rèn)為可以在其他非運(yùn)行線程調(diào)用這些方法。


本文鏈接:Java高并發(fā)綜合
作者:猴子007
出處:https://monkeysayhi.github.io
本文基于 知識(shí)共享署名-相同方式共享 4.0 國(guó)際許可協(xié)議發(fā)布,歡迎轉(zhuǎn)載,演繹或用于商業(yè)目的,但是必須保留本文的署名及鏈接。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 曾經(jīng),我在面試Java研發(fā)實(shí)習(xí)生時(shí)最常聽(tīng)到的一句話就是: 搞Java怎么能不學(xué)并發(fā)呢? 沒(méi)錯(cuò),真的是經(jīng)過(guò)了面試官的...
    野夢(mèng)M閱讀 493評(píng)論 0 2
  • Java SE 基礎(chǔ): 封裝、繼承、多態(tài) 封裝: 概念:就是把對(duì)象的屬性和操作(或服務(wù))結(jié)合為一個(gè)獨(dú)立的整體,并盡...
    Jayden_Cao閱讀 2,259評(píng)論 0 8
  • 從三月份找實(shí)習(xí)到現(xiàn)在,面了一些公司,掛了不少,但最終還是拿到小米、百度、阿里、京東、新浪、CVTE、樂(lè)視家的研發(fā)崗...
    時(shí)芥藍(lán)閱讀 42,869評(píng)論 11 349
  • 下面是我自己收集整理的Java線程相關(guān)的面試題,可以用它來(lái)好好準(zhǔn)備面試。 參考文檔:-《Java核心技術(shù) 卷一》-...
    阿呆變Geek閱讀 15,149評(píng)論 14 507
  • ——鴻鸞禧 結(jié)婚到底為了什么,很多人就這么渾渾噩噩的過(guò)了一輩子。 張愛(ài)玲筆下的玉清因?yàn)榻Y(jié)婚,終于能夠發(fā)泄自己的購(gòu)物...
    檸懵噠噠閱讀 592評(píng)論 0 0

友情鏈接更多精彩內(nèi)容