以下這些面試題是我對(duì)著多線程的知識(shí)點(diǎn)自己一點(diǎn)點(diǎn)總結(jié)下來的焊傅,如果有不全的地方歡迎大家留言一起探討慨蛙。
多線程:是指這個(gè)程序運(yùn)行時(shí)產(chǎn)生了不止一個(gè)線程
并行:多個(gè)CPU同時(shí)處理一段邏輯
并發(fā):通過cpu調(diào)度算法解寝,讓用戶覺得是在同時(shí)進(jìn)行锚烦,但cpu內(nèi)部并不是真正的同時(shí)
多線程是實(shí)現(xiàn)并發(fā)機(jī)制的一種有效手段
1觅闽、新建:在new Thread時(shí),jvm會(huì)像普通對(duì)象一樣給線程分配內(nèi)存涮俄,并初始化其成員變量的值
2蛉拙、就緒:當(dāng)執(zhí)行了start方法后,該線程便處于就緒狀態(tài)彻亲,但是它并沒有運(yùn)行孕锄,只是表示可以運(yùn)行了吮廉。(注意,當(dāng)線程被阻塞后重新恢復(fù)后畸肆,必須先經(jīng)過就緒狀態(tài))
3宦芦、運(yùn)行:如果就緒狀態(tài)的線程獲得了CPU,那么執(zhí)行run方法后轴脐,該線程會(huì)處于運(yùn)行狀態(tài)调卑,
如果計(jì)算機(jī)只有一個(gè)CPU,那么在任何時(shí)刻大咱,只有一個(gè)線程處于運(yùn)行狀態(tài)恬涧。在多處理器的機(jī)器上,會(huì)有多個(gè)線程并行執(zhí)行徽级。當(dāng)線程數(shù)大于CPU數(shù)時(shí)气破,依然會(huì)有多個(gè)線程在同一CPU上輪換(即并發(fā))的現(xiàn)象。
4餐抢、阻塞:可以理解為暫停執(zhí)行該線程
5现使、死亡:run或call執(zhí)行完成后;拋出未捕獲的異常后旷痕;直接調(diào)用stop方法后碳锈,線程處于死亡狀態(tài)
有哪些方法可以讓線程進(jìn)入阻塞狀態(tài),然后又如何恢復(fù)線程到運(yùn)行狀態(tài)
使線程進(jìn)入阻塞狀態(tài)的方法:
1欺抗、調(diào)用sleep方法主動(dòng)放棄所占用的處理器資源
2售碳、調(diào)用了一個(gè)阻塞式的IO方法:如等待某個(gè)輸入輸出流的完成
3、線程試圖得到一個(gè)鎖绞呈,而該鎖正在被其他線程調(diào)用
4贸人、線程在等待某個(gè)通知(notify)
5、調(diào)用suspend方法暫停了線程佃声,暫停后的線程必須通過resume方法來恢復(fù)艺智,容易造成死鎖,一般不用
恢復(fù)線程到運(yùn)行狀態(tài):
1圾亏、sleep方法的線程經(jīng)過了指定的時(shí)間
2十拣、阻塞式的IO方法已經(jīng)返回
3、成功的獲取到了試圖得到的鎖
4夭问、線程正在等等某個(gè)通知時(shí),其他線程發(fā)出了一個(gè)通知
5埠胖、調(diào)用了resume方法
可以調(diào)用線程對(duì)象的isAlive方法,當(dāng)處于就緒、運(yùn)行锤悄、阻塞時(shí),返回true隶症。當(dāng)處于新建或死亡時(shí),返回false
能不能對(duì)一個(gè)死亡的線程重新調(diào)用start方法讓它重新啟動(dòng):
不能,死亡就是死亡,該線程將不可再次作為線程執(zhí)行
線程從就緒狀態(tài)到運(yùn)行狀態(tài)不受程序控制,而是根據(jù)系統(tǒng)線程調(diào)用所決定或听,當(dāng)處于就緒狀態(tài)的線程獲得到處理器資源時(shí),會(huì)進(jìn)入運(yùn)行狀態(tài)粱腻。而當(dāng)處于運(yùn)行狀態(tài)的線程失去處理器資源時(shí),會(huì)處于就緒狀態(tài)。但是yield方法可以使線程從運(yùn)行狀態(tài)轉(zhuǎn)入就緒狀態(tài)
Java程序每次運(yùn)行至少啟動(dòng)幾個(gè)線程
兩個(gè)線程,一個(gè)是主線程,一個(gè)是垃圾回收機(jī)制的線程
如果沒有啟動(dòng)start而是直接啟動(dòng)線程的run方法會(huì)怎么樣:
虛擬機(jī)會(huì)把這個(gè)線程當(dāng)做一個(gè)普通的對(duì)象昂勒,而run方法也會(huì)被看做是一個(gè)普通的對(duì)象中的方法
讓一個(gè)線程等待另一個(gè)線程完成,比如:我創(chuàng)建了一個(gè)MyThread線程:
MyThread myThread = new MyThread();
然后在main()方法中調(diào)用(本質(zhì)是在main線程中調(diào)用)myThread.join()方法,那么main線程會(huì)進(jìn)入阻塞狀態(tài)直到MyThread線程中的run方法的執(zhí)行完畢后才會(huì)繼續(xù)執(zhí)行。
有一種線程,是在后臺(tái)運(yùn)行的疏哗,它的任務(wù)是為其他線程提供服務(wù)吗氏,又稱為“守護(hù)線程”关面,
比如JVM的垃圾回收線程就是一個(gè)后臺(tái)線程缩抡。后臺(tái)線程有一個(gè)特征压真,就是如果所有前臺(tái)線程全部死亡,那么后臺(tái)線程會(huì)自動(dòng)死亡蘑险。
可以讓線程暫停一段時(shí)間,當(dāng)線程進(jìn)入睡眠狀態(tài)后滴肿,該線程不會(huì)獲得執(zhí)行機(jī)會(huì),即使系統(tǒng)中沒有其他可以執(zhí)行的線程佃迄,處于sleep中的線程也不會(huì)執(zhí)行泼差,直到睡眠結(jié)束
最大的區(qū)別是,sleep()在睡眠后不會(huì)釋放掉鎖呵俏,而wait()在睡眠后會(huì)釋放掉鎖堆缘。
將當(dāng)前正在執(zhí)行的線程暫停,但是是將線程進(jìn)入到就緒狀態(tài)普碎,釋放CPU資源
線程優(yōu)先級(jí)是什么吼肥,如何改變線程優(yōu)先級(jí):
每個(gè)線程都有一定的優(yōu)先級(jí),優(yōu)先級(jí)高的線程會(huì)獲得到更多的執(zhí)行機(jī)會(huì)麻车。每個(gè)線程的優(yōu)先級(jí)和創(chuàng)建它的父線程的優(yōu)先級(jí)相同缀皱,Thread提供了setPriority方法來設(shè)定優(yōu)先級(jí),參數(shù)范圍為1-10
什么是線程安全动猬,什么是線程不安全(synchronized的作用或者由來)
線程的run方法不具有同步安全性唆鸡,當(dāng)多個(gè)線程同時(shí)處理一個(gè)共享數(shù)據(jù)時(shí),可能導(dǎo)致數(shù)據(jù)混亂枣察,這就是線程不安全。所以引入了synchronized來解決此問題。被synchronized鎖定的同步代碼塊序目,只能同時(shí)被一個(gè)線程獲取到臂痕,當(dāng)該線程執(zhí)行完此方法后,會(huì)釋放掉該鎖猿涨,這時(shí)其他線程才可以繼續(xù)獲取該鎖握童。Synchronized()括號(hào)內(nèi)可以傳任何參數(shù),即Obj類型叛赚,但通常是被共同訪問的共享資源來作為同步監(jiān)視器
被synchronized修飾的方法,成為同步方法俺附,如:
Public synchronized void getPerson(){};
當(dāng)內(nèi)部類中的代碼塊被synchronized(this)修飾時(shí)肥卡,請(qǐng)問這個(gè)this指的是內(nèi)部類還是其父類:
內(nèi)部類
當(dāng)一個(gè)線程訪問某個(gè)類中的被synchronized修飾的方法時(shí),另一個(gè)線程可以訪問該類中其他沒有被synchronized修飾的方法嗎:
可以
當(dāng)一個(gè)線程訪問某個(gè)類中的被synchronized修飾的方法時(shí)事镣,另一個(gè)線程可以訪問該類中其他被synchronized修飾的方法嗎:
不可以步鉴,因?yàn)镴ava中的每個(gè)對(duì)象都有一個(gè)鎖(lock),或者叫做監(jiān)視器(monitor)璃哟,當(dāng)一個(gè)線程訪問某個(gè)對(duì)象的synchronized方法時(shí)氛琢,將該對(duì)象上鎖,其他任何線程都無法再去訪問該對(duì)象的synchronized方法了(這里是指所有的同步方法随闪,而不僅僅是同一個(gè)方法)阳似,直到之前的那個(gè)線程執(zhí)行方法完畢后(或者是拋出了異常),才將該對(duì)象的鎖釋放掉铐伴,其他線程才有可能再去訪問該對(duì)象的synchronized方法撮奏。
1、當(dāng)前線程的同步方法盛杰,同步代碼塊執(zhí)行結(jié)束
2即供、出現(xiàn)了未處理的Error或Exception,導(dǎo)致代碼塊結(jié)束
3青自、當(dāng)線程正在執(zhí)行加鎖的代碼塊或同步方法時(shí)延窜,調(diào)用了wait()方法逆瑞,那么會(huì)使線程進(jìn)入阻塞狀態(tài),并且釋放鎖
4获高、注意:當(dāng)線程正在執(zhí)行加鎖的代碼塊或同步方法時(shí)念秧,調(diào)用sleep()方法或者yield()方法摊趾,不會(huì)釋放鎖
當(dāng)所有線程處于阻塞狀態(tài)漩绵,整個(gè)程序沒有發(fā)生異常渐行,也不會(huì)有任何提示祟印,就進(jìn)入了死鎖狀態(tài)粟害;
當(dāng)兩個(gè)線程相互等待對(duì)方釋放鎖時(shí),就會(huì)發(fā)生死鎖套鹅。
什么是Lock鎖卓鹿,和synchronized有什么區(qū)別:
1吟孙、Lock是一個(gè)接口杰妓,而synchronized是Java中的關(guān)鍵字碘勉,synchronized是內(nèi)置的語言實(shí)現(xiàn)验靡;
2雏节、synchronized在發(fā)生異常時(shí)矾屯,會(huì)自動(dòng)釋放線程占有的鎖,因此不會(huì)導(dǎo)致死鎖現(xiàn)象發(fā)生产禾;而Lock在發(fā)生異常時(shí)亚情,如果沒有主動(dòng)通過unLock()去釋放鎖楞件,則很可能造成死鎖現(xiàn)象裳瘪,因此使用Lock時(shí)需要在finally塊中釋放鎖彭羹;
3派殷、Lock可以通過tryLock()方法知道是否成功獲取到鎖,但是synchronized不行
4毡惜、Lock可以讓等待鎖的線程響應(yīng)中斷经伙,而synchronized卻不行,使用synchronized時(shí)辜梳,等待的線程會(huì)一直等待下去作瞄,不能夠響應(yīng)中斷危纫;
5、Lock可以提高多個(gè)線程進(jìn)行讀操作的效率(讀寫鎖)瞒大。
6透敌、Lock可以實(shí)現(xiàn)公平鎖酗电,Synchronized不保證公平性撵术。
wait()/notify()機(jī)制:
ThreadA處于阻塞狀態(tài)(wait方法)嫩与,ThreadB在run方法中對(duì)某個(gè)數(shù)據(jù)做操作划滋,當(dāng)數(shù)據(jù)達(dá)到我設(shè)定的某個(gè)條件時(shí)古毛,通過notify()來喚醒ThreadA稻薇。
好處:提高了CPU的利用率
缺點(diǎn):如果通知過早塞椎,會(huì)打亂執(zhí)行的邏
其他通信方法請(qǐng)參考:
http://www.importnew.com/26850.html
為了避免重復(fù)創(chuàng)建線程睛低,線程池可以的出現(xiàn)可以讓線程進(jìn)行復(fù)用骂铁,當(dāng)需要時(shí)罩抗,從線程池中取出一個(gè)線程套蒂,當(dāng)工作完成后,并不是直接關(guān)閉線程婴洼,而是將線程歸還給線程池供其他任務(wù)使用柬采。
newFixedThreadPool:
固定大小的線程池警没,可以指定線程池的大小,該線程池corePoolSize和maximumPoolSize相等押搪,阻塞隊(duì)列使用的是LinkedBlockingQueue大州,大小為整數(shù)最大值垂谢。
newSingleThreadExecutor:
單個(gè)線程線程池,只有一個(gè)線程的線程池滥朱,阻塞隊(duì)列使用的是LinkedBlockingQueue,若有多余的任務(wù)提交到線程池中根暑,則會(huì)被暫存到阻塞隊(duì)列,待空閑時(shí)再去執(zhí)行徙邻。按照先入先出的順序執(zhí)行任務(wù)排嫌。
newCachedThreadPool:
緩存線程池,緩存的線程默認(rèn)存活60秒缰犁。線程的核心池corePoolSize大小為0淳地,核心池最大為Integer.MAX_VALUE,阻塞隊(duì)列使用的是SynchronousQueue。是一個(gè)直接提交的阻塞隊(duì)列帅容,他總會(huì)迫使線程池增加新的線程去執(zhí)行新的任務(wù)颇象。在沒有任務(wù)執(zhí)行時(shí),當(dāng)線程的空閑時(shí)間超過keepAliveTime(60秒)并徘,則工作線程將會(huì)終止被回收遣钳,當(dāng)提交新任務(wù)時(shí)荐开,如果沒有空閑線程,則創(chuàng)建新線程執(zhí)行任務(wù)佣渴,會(huì)導(dǎo)致一定的系統(tǒng)開銷砂竖。如果同時(shí)又大量任務(wù)被提交置济,而且任務(wù)執(zhí)行的時(shí)間不是特別快查库,那么線程池便會(huì)新增出等量的線程池處理任務(wù),這很可能會(huì)很快耗盡系統(tǒng)的資源剂府。
newScheduledThreadPool:
定時(shí)線程池,該線程池可用于周期性地去執(zhí)行任務(wù),通常用于周期性的同步數(shù)據(jù)。
鎖池和等待池:
1、鎖池:假設(shè)線程A已經(jīng)擁有了某個(gè)對(duì)象(注意:不是類)的鎖,而其它的線程想要調(diào)用這個(gè)對(duì)象的某個(gè)synchronized方法(或者synchronized塊),由于這些線程在進(jìn)入對(duì)象的synchronized方法之前必須先獲得該對(duì)象的鎖的擁有權(quán)幅恋,但是該對(duì)象的鎖目前正被線程A擁有品追,所以這些線程就進(jìn)入了該對(duì)象的鎖池中泞莉。
2、等待池:假設(shè)一個(gè)線程A調(diào)用了某個(gè)對(duì)象的wait()方法钉疫,線程A就會(huì)釋放該對(duì)象的鎖后咨油,進(jìn)入到了該對(duì)象的等待池中
notify和notifyAll的區(qū)別
1冀膝、如果線程調(diào)用了對(duì)象的 wait()方法,那么線程便會(huì)處于該對(duì)象的等待池中,等待池中的線程不會(huì)去競(jìng)爭(zhēng)該對(duì)象的鎖巫延。
2火俄、當(dāng)有線程調(diào)用了對(duì)象的 notifyAll()方法(喚醒所有 wait 線程)或 notify()方法(只隨機(jī)喚醒一個(gè) wait 線程)讲冠,被喚醒的的線程便會(huì)進(jìn)入該對(duì)象的鎖池中,鎖池中的線程會(huì)去競(jìng)爭(zhēng)該對(duì)象鎖谱仪。也就是說嗦随,調(diào)用了notify后只要一個(gè)線程會(huì)由等待池進(jìn)入鎖池蜻直,而notifyAll會(huì)將該對(duì)象等待池內(nèi)的所有線程移動(dòng)到鎖池中王悍,等待鎖競(jìng)爭(zhēng)
3厚脉、優(yōu)先級(jí)高的線程競(jìng)爭(zhēng)到對(duì)象鎖的概率大胶惰,假若某線程沒有競(jìng)爭(zhēng)到該對(duì)象鎖傻工,它還會(huì)留在鎖池中,唯有線程再次調(diào)用 wait()方法,它才會(huì)重新回到等待池中中捆。而競(jìng)爭(zhēng)到對(duì)象鎖的線程則繼續(xù)往下執(zhí)行鸯匹,直到執(zhí)行完了 synchronized 代碼塊,它會(huì)釋放掉該對(duì)象鎖泄伪,這時(shí)鎖池中的線程會(huì)繼續(xù)競(jìng)爭(zhēng)該對(duì)象鎖