《面試1v1》Java多線程

我是 javapub,一名 Markdown
程序員從?????,八股文種子選手。
《面試1v1》更新中...?
面試官: 說(shuō)說(shuō)你對(duì)多線程的理解?
候選人: 多線程就是同時(shí)運(yùn)行多個(gè)線程,實(shí)現(xiàn)一件事的并行處理。比如開(kāi)個(gè)程序,同時(shí)下載多個(gè)文件,同時(shí)處理多個(gè)客戶端請(qǐng)求等等。面試官:那什么是線程安全的?舉個(gè)例子?
候選人: 線程安全就是多個(gè)線程訪問(wèn)同一個(gè)對(duì)象或調(diào)用同一方法時(shí),對(duì)象或方法內(nèi)部的狀態(tài)能保證正確。舉個(gè)例子,String 是線程安全的,因?yàn)?String 內(nèi)部的 char 數(shù)組是final的,不可變的。
public?final?class?String?{
????private?final?char?value[];
}
面試官:ArrayList線程安全嗎?
候選人: ArrayList 不是線程安全的,因?yàn)?
transient?Object[]?elementData;?//?elementData?可以改變?
如果多個(gè)線程同時(shí)訪問(wèn)一個(gè) ArrayList,其中一個(gè)線程正在擴(kuò)容數(shù)組,這時(shí)另一個(gè)線程在讀或添加元素,很可能引起空指針或者越界異常。面試官:HashMap 呢?線程安全嗎?
候選人: HashMap 也不是線程安全的,跟 ArrayList 一樣,HashMap 在多線程下也可能產(chǎn)生死循環(huán)、數(shù)據(jù)丟失等問(wèn)題。因?yàn)?
transient?Node<K,V>[]?table;
并發(fā)情況下,比如兩個(gè)線程同時(shí) put 新鍵值對(duì),都重新擴(kuò)容了數(shù)組,都做舊數(shù)組到新數(shù)組的遷移工作,這就會(huì)產(chǎn)生數(shù)據(jù)丟失的問(wèn)題。面試官:那如何解決 HashMap 的線程安全問(wèn)題?
候選人: 有幾種常見(jiàn)的解決HashMap線程不安全的方法:
Collections.synchronizedMap():返回一個(gè)線程安全的 HashMap,內(nèi)部使用鎖機(jī)制同步訪問(wèn) HashMap。
ConcurrentHashMap:Java 7 發(fā)布的線程安全的 HashMap。內(nèi)部使用鎖分段技術(shù)實(shí)現(xiàn)線程安全,并發(fā)度很高。
Hashtable:Hashtable 是遺留的線程安全 HashMap,內(nèi)部也使用同一把鎖,并發(fā)度低。不推薦使用。
外包裝:可以使用 Lock 或者 synchronized 關(guān)鍵字對(duì) HashMap 進(jìn)行外包裝,實(shí)現(xiàn)線程安全。
面試官:謝謝,內(nèi)容很詳細(xì)!總結(jié)一下,線程安全對(duì)我們來(lái)說(shuō)很重要,在編寫(xiě)代碼時(shí)要時(shí)刻考慮清楚。
候選人: 是的,總結(jié)如下:
多線程環(huán)境下,需謹(jǐn)慎使用非線程安全的類。如 ArrayList、HashMap。
盡量選擇Java提供的線程安全類,如String、ConcurrentHashMap。
如果必須使用非線程安全的類,需要對(duì)其進(jìn)行額外的同步措施,如加鎖或者使用同步包裝類。
在編寫(xiě)代碼時(shí),要時(shí)刻考慮對(duì)象或方法的狀態(tài)是否在多線程下也能保持一致和正確。這就是我們提到的“線程安全”。 多謝面試官的提問(wèn),讓我對(duì)Java多線程和線程安全有一個(gè)比較全面和系統(tǒng)的復(fù)習(xí),這些內(nèi)容對(duì)我以后的學(xué)習(xí)和工作會(huì)很有幫助。
面試官: 說(shuō)說(shuō) wait() notify() notifyAll() 的區(qū)別?
候選人: 這三個(gè)方法都是用來(lái)協(xié)調(diào)線程間通信的。區(qū)別如下:
wait():讓當(dāng)前線程等待,直到其他線程調(diào)用 notify() 方法通知,或經(jīng)過(guò)指定的時(shí)間后重新等待。
notify():喚醒等待在此對(duì)象上的一個(gè)線程。如果有多個(gè)線程等待,則喚醒優(yōu)先級(jí)最高的線程。
notifyAll():喚醒等待在此對(duì)象上的所有線程。
面試官:舉個(gè)例子解釋下?
候選人: 這里是一個(gè)經(jīng)典的生產(chǎn)者消費(fèi)者模型來(lái)解釋這三個(gè)方法:
public?class?ProducerConsumer?{
????private?int?product?=?0;
????private?boolean?isProduced?=?false;
????public?synchronized?void?produce()?{
????????//?等待,直到產(chǎn)品被消費(fèi)
????????while?(isProduced)?{
????????????try?{
????????????????wait();
????????????}?catch?(InterruptedException?e)?{
????????????????e.printStackTrace();
????????????}
????????}
????????//?生產(chǎn)產(chǎn)品
????????product++;
????????isProduced?=?true;
????????//?通知消費(fèi)者消費(fèi)產(chǎn)品
????????notifyAll();
????}
????public?synchronized?void?consume()?{
????????//?等待,直到有產(chǎn)品生產(chǎn)
????????while?(!isProduced)?{
????????????try?{
????????????????wait();
????????????}?catch?(InterruptedException?e)?{
????????????????e.printStackTrace();
????????????}?????
????????}
????????//?消費(fèi)產(chǎn)品
????????product--;
????????isProduced?=?false;
????????//?通知生產(chǎn)者生產(chǎn)產(chǎn)品
????????notifyAll();
????}
}
這里 wait() 方法使生產(chǎn)線程和消費(fèi)線程在產(chǎn)品未就緒時(shí)等待,notifyAll() 方法在產(chǎn)品就緒時(shí)喚醒等待線程。
面試官:sleep() 方法和 wait() 方法有什么區(qū)別?
候選人: sleep() 和 wait() 的主要區(qū)別在于:
wait() 方法釋放鎖,sleep() 方法不釋放鎖。
wait() 方法通常被用于線程間通信,sleep() 方法用于暫停線程指定時(shí)間。
wait() 方法可以在沒(méi)有指定時(shí)間的情況下一直等待,sleep() 方法必須指定等待時(shí)間。
wait() 方法被喚醒后必須重新獲取鎖,sleep() 方法睡醒后直接繼續(xù)執(zhí)行。 所以簡(jiǎn)單來(lái)說(shuō):wait() 可以用于線程間的同步,sleep() 主要用于暫停線程指定時(shí)間。
面試官:完美!謝謝你,這些知識(shí)點(diǎn)解釋的很透徹。
候選人: 不客氣,多謝面試官的提問(wèn),讓我對(duì) wait() notify() notifyAll() 以及它們與 sleep() 的區(qū)別有了更深的理解,這些都是非常重要的多線程知識(shí)點(diǎn),我會(huì)繼續(xù)加深理解并運(yùn)用的!
最近我在更新《面試1v1》系列文章,主要以場(chǎng)景化的方式,講解我們?cè)诿嬖囍杏龅降膯?wèn)題,致力于讓每一位工程師拿到自己心儀的offer,感興趣可以關(guān)注JavaPub追更!

??目錄合集:
Gitee:https://gitee.com/rodert/JavaPub
GitHub:https://github.com/Rodert/JavaPub
http://javapub.net.cn