多線程面試

0、Java中多線程同步是什么?
****在多線程程序下,同步能控制對(duì)共享資源的訪問(wèn)。如果沒(méi)有同步,當(dāng)一個(gè)Java線程在修改一個(gè)共享變量時(shí),另外一個(gè)線程正在使用或者更新同一個(gè)變量,這樣容易導(dǎo)致程序出現(xiàn)錯(cuò)誤的結(jié)果。

1、解釋實(shí)現(xiàn)多線程的幾種方法?
****一Java線程可以實(shí)現(xiàn)Runnable接口或者繼承Thread類(lèi)來(lái)實(shí)現(xiàn),當(dāng)你打算多重繼承時(shí),優(yōu)先選擇實(shí)現(xiàn)Runnable。

2、Thread.start()與Thread.run()有什么區(qū)別?
****Thread.start()方法(native)啟動(dòng)線程,使之進(jìn)入就緒狀態(tài),當(dāng)cpu分配時(shí)間該線程時(shí),由JVM調(diào)度執(zhí)行run()方法。


3、為什么需要run()和start()方法,我們可以只用run()方法來(lái)完成任務(wù)嗎?
****我們需要run()&start()這兩個(gè)方法是因?yàn)镴VM創(chuàng)建一個(gè)單獨(dú)的線程不同于普通方法的調(diào)用,所以這項(xiàng)工作由線程的start方法來(lái)完成,start由本地方法實(shí)現(xiàn),需要顯示地被調(diào)用,使用這倆個(gè)方法的另外一個(gè)好處是任何一個(gè)對(duì)象都可以作為線程運(yùn)行,只要實(shí)現(xiàn)了Runnable接口,這就避免因繼承了Thread類(lèi)而造成的Java的多繼承問(wèn)題。

4、什么是ThreadLocal類(lèi),怎么使用它?
****ThreadLocal是一個(gè)線程級(jí)別的局部變量,并非“本地線程”。ThreadLocal為每個(gè)使用該變量的線程提供了一個(gè)獨(dú)立的變量副本,每個(gè)線程修改副本時(shí)不影響其它線程對(duì)象的副本(譯者注)。
下面是線程局部變量(ThreadLocal variables)的關(guān)鍵點(diǎn):
一個(gè)線程局部變量(ThreadLocal variables)為每個(gè)線程方便地提供了一個(gè)單獨(dú)的變量。
ThreadLocal實(shí)例通常作為靜態(tài)的私有的(private static)字段出現(xiàn)在一個(gè)類(lèi)中,這個(gè)類(lèi)用來(lái)關(guān)聯(lián)一個(gè)線程。
當(dāng)多個(gè)線程訪問(wèn)ThreadLocal實(shí)例時(shí),每個(gè)線程維護(hù)ThreadLocal提供的獨(dú)立的變量副本。
常用的使用可在DAO模式中見(jiàn)到,當(dāng)DAO類(lèi)作為一個(gè)單例類(lèi)時(shí),數(shù)據(jù)庫(kù)鏈接(connection)被每一個(gè)線程獨(dú)立的維護(hù),互不影響。(基于線程的單例)
ThreadLocal難于理解,下面這些引用連接有助于你更好的理解它。
Good article on ThreadLocal on IBM DeveloperWorks 》、《理解ThreadLocal》、《Managing data : Good example》、《Refer Java API Docs

5、什么時(shí)候拋出InvalidMonitorStateException異常,為什么?
****調(diào)用wait()/notify()/notifyAll()中的任何一個(gè)方法時(shí),如果當(dāng)前線程沒(méi)有獲得該對(duì)象的鎖,那么就會(huì)拋出IllegalMonitorStateException的異常(也就是說(shuō)程序在沒(méi)有執(zhí)行對(duì)象的任何同步塊或者同步方法時(shí),仍然嘗試調(diào)用wait()/notify()/notifyAll()時(shí))。由于該異常是RuntimeExcpetion的子類(lèi),所以該異常不一定要捕獲(盡管你可以捕獲只要你愿意).作為RuntimeException,此類(lèi)異常不會(huì)在wait(),notify(),notifyAll()的方法簽名提及。

6、Sleep()、suspend()和wait()之間有什么區(qū)別?
Thread.sleep()使當(dāng)前線程在指定的時(shí)間處于“非運(yùn)行”(Not Runnable)狀態(tài)。線程一直持有對(duì)象的監(jiān)視器。比如一個(gè)線程當(dāng)前在一個(gè)同步塊或同步方法中,其它線程不能進(jìn)入該塊或方法中。如果另一線程調(diào)用了interrupt()方法,它將喚醒那個(gè)“睡眠的”線程。
注意:sleep()是一個(gè)靜態(tài)方法。這意味著只對(duì)當(dāng)前線程有效,一個(gè)常見(jiàn)的錯(cuò)誤是調(diào)用t.sleep(),(這里的t是一個(gè)不同于當(dāng)前線程的線程)。即便是執(zhí)行t.sleep(),也是當(dāng)前線程進(jìn)入睡眠,而不是t線程。t.suspend()是過(guò)時(shí)的方法,使用suspend()導(dǎo)致線程進(jìn)入停滯狀態(tài),該線程會(huì)一直持有對(duì)象的監(jiān)視器,suspend()容易引起死鎖問(wèn)題。
object.wait()使當(dāng)前線程出于“不可運(yùn)行”狀態(tài),和sleep()不同的是wait是object的方法而不是thread。調(diào)用object.wait()時(shí),線程先要獲取這個(gè)對(duì)象的對(duì)象鎖,當(dāng)前線程必須在鎖對(duì)象保持同步,把當(dāng)前線程添加到等待隊(duì)列中,隨后另一線程可以同步同一個(gè)對(duì)象鎖來(lái)調(diào)用object.notify(),這樣將喚醒原來(lái)等待中的線程,然后釋放該鎖?;旧蟱ait()/notify()與sleep()/interrupt()類(lèi)似,只是前者需要獲取對(duì)象鎖。

7、在靜態(tài)方法上使用同步時(shí)會(huì)發(fā)生什么事?
****同步靜態(tài)方法時(shí)會(huì)獲取該類(lèi)的“Class”對(duì)象,所以當(dāng)一個(gè)線程進(jìn)入同步的靜態(tài)方法中時(shí),線程監(jiān)視器獲取類(lèi)本身的對(duì)象鎖,其它線程不能進(jìn)入這個(gè)類(lèi)的任何靜態(tài)同步方法。它不像實(shí)例方法,因?yàn)槎鄠€(gè)線程可以同時(shí)訪問(wèn)不同實(shí)例同步實(shí)例方法。

8、當(dāng)一個(gè)同步方法已經(jīng)執(zhí)行,線程能夠調(diào)用對(duì)象上的非同步實(shí)例方法嗎?
****可以,一個(gè)非同步方法總是可以被調(diào)用而不會(huì)有任何問(wèn)題。實(shí)際上,Java沒(méi)有為非同步方法做任何檢查,鎖對(duì)象僅僅在同步方法或者同步代碼塊中檢查。如果一個(gè)方法沒(méi)有聲明為同步,即使你在使用共享數(shù)據(jù)Java照樣會(huì)調(diào)用,而不會(huì)做檢查是否安全,所以在這種情況下要特別小心。一個(gè)方法是否聲明為同步取決于臨界區(qū)訪問(wèn)(critial section access),如果方法不訪問(wèn)臨界區(qū)(共享資源或者數(shù)據(jù)結(jié)構(gòu))就沒(méi)必要聲明為同步的。
下面有一個(gè)示例說(shuō)明:Common類(lèi)有兩個(gè)方法synchronizedMethod1()和method1(),MyThread類(lèi)在獨(dú)立的線程中調(diào)用這兩個(gè)方法。
'''
public
class
Common {

public
synchronized
void
synchronizedMethod1() {

System.out.println(
"synchronizedMethod1 called"
);

try
{

Thread.sleep(
1000
);

}
catch
(InterruptedException e) {

e.printStackTrace();

}

System.out.println(
"synchronizedMethod1 done"
);

}

public
void
method1() {

System.out.println(
"Method 1 called"
);

try
{

Thread.sleep(
1000
);

}
catch
(InterruptedException e) {

e.printStackTrace();

}

System.out.println(
"Method 1 done"
);

}

}

'''
'''
public
class
MyThread
extends
Thread {

private
int
id =
0
;

private
Common common;

public
MyThread(String name,
int
no, Common object) {

super
(name);

common = object;

id = no;

}

public
void
run() {

System.out.println(
"Running Thread"

this
.getName());

try
{

if
(id ==
0
) {

common.synchronizedMethod1();

}
else
{

common.method1();

}

}
catch
(Exception e) {

e.printStackTrace();

}

}

public
static
void
main(String[] args) {

Common c =
new
Common();

MyThread t1 =
new
MyThread(
"MyThread-1"
,
0
, c);

MyThread t2 =
new
MyThread(
"MyThread-2"
,
1
, c);

t1.start();

t2.start();

}

}

'''

這里是程序的輸出:
1
2
3
4
5
6

Running ThreadMyThread-
1

synchronizedMethod1 called

Running ThreadMyThread-
2

Method
1
called

synchronizedMethod1 done

Method
1
done

結(jié)果表明即使synchronizedMethod1()方法執(zhí)行了,method1()也會(huì)被調(diào)用。

9、 在一個(gè)對(duì)象上兩個(gè)線程可以調(diào)用兩個(gè)不同的同步實(shí)例方法嗎?
****不能,因?yàn)橐粋€(gè)對(duì)象已經(jīng)同步了實(shí)例方法,線程獲取了對(duì)象的對(duì)象鎖。所以只有執(zhí)行完該方法釋放對(duì)象鎖后才能執(zhí)行其它同步方法??聪旅娲a示例非常清晰:Common 類(lèi) 有synchronizedMethod1()和synchronizedMethod2()方法,MyThread調(diào)用這兩個(gè)方法。
'''
public
class
Common {

public
synchronized
void
synchronizedMethod1() {

System.out.println(
"synchronizedMethod1 called"
);

try
{

Thread.sleep(
1000
);

}
catch
(InterruptedException e) {

e.printStackTrace();

}

System.out.println(
"synchronizedMethod1 done"
);

}

public
synchronized
void
synchronizedMethod2() {

System.out.println(
"synchronizedMethod2 called"
);

try
{

Thread.sleep(
1000
);

}
catch
(InterruptedException e) {

e.printStackTrace();

}

System.out.println(
"synchronizedMethod2 done"
);

}

}

'''
'''

public
class
MyThread
extends
Thread {

private
int
id =
0
;

private
Common common;

public
MyThread(String name,
int
no, Common object) {

super
(name);

common = object;

id = no;

}

public
void
run() {

System.out.println(
"Running Thread"

this
.getName());

try
{

if
(id ==
0
) {

common.synchronizedMethod1();

}
else
{

common.synchronizedMethod2();

}

}
catch
(Exception e) {

e.printStackTrace();

}

}

public
static
void
main(String[] args) {

Common c =
new
Common();

MyThread t1 =
new
MyThread(
"MyThread-1"
,
0
, c);

MyThread t2 =
new
MyThread(
"MyThread-2"
,
1
, c);

t1.start();

t2.start();

}

}

10、 什么是死鎖
****死鎖就是兩個(gè)或兩個(gè)以上的線程被無(wú)限的阻塞,線程之間相互等待所需資源。這種情況可能發(fā)生在當(dāng)兩個(gè)線程嘗試獲取其它資源的鎖,而每個(gè)線程又陷入無(wú)限等待其它資源鎖的釋放,除非一個(gè)用戶進(jìn)程被終止。就JavaAPI而言,線程死鎖可能發(fā)生在一下情況。
●當(dāng)兩個(gè)線程相互調(diào)用Thread.join()
●當(dāng)兩個(gè)線程使用嵌套的同步塊,一個(gè)線程占用了另外一個(gè)線程必需的鎖,互相等待時(shí)被阻塞就有可能出現(xiàn)死鎖。

11、什么是線程餓死,什么是活鎖?
****線程餓死和活鎖雖然不想是死鎖一樣的常見(jiàn)問(wèn)題,但是對(duì)于并發(fā)編程的設(shè)計(jì)者來(lái)說(shuō)就像一次邂逅一樣。
當(dāng)所有線程阻塞,或者由于需要的資源無(wú)效而不能處理,不存在非阻塞線程使資源可用。JavaAPI中線程活鎖可能發(fā)生在以下情形:
●當(dāng)所有線程在程序中執(zhí)行Object.wait(0),參數(shù)為0的wait方法。程序?qū)l(fā)生活鎖直到在相應(yīng)的對(duì)象上有線程調(diào)用Object.notify()或者Object.notifyAll()。
●當(dāng)所有線程卡在無(wú)限循環(huán)中。

來(lái)源:http://blog.jobbole.com/18571/

最后編輯于
?著作權(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)容

  • 1. 進(jìn)程和線程之間有什么不同? 一個(gè)進(jìn)程是一個(gè)獨(dú)立(self contained)的運(yùn)行環(huán)境,它可以被看作一個(gè)程...
    Sophie12138閱讀 240評(píng)論 0 3
  • Java多線程學(xué)習(xí) [-] 一擴(kuò)展javalangThread類(lèi) 二實(shí)現(xiàn)javalangRunnable接口 三T...
    影馳閱讀 3,112評(píng)論 1 18
  • 本文主要講了java中多線程的使用方法、線程同步、線程數(shù)據(jù)傳遞、線程狀態(tài)及相應(yīng)的一些線程函數(shù)用法、概述等。 首先講...
    李欣陽(yáng)閱讀 2,600評(píng)論 1 15
  • 下班后,笑笑超級(jí)開(kāi)心,屁顛的背著包往公交站走去,好像那接近一個(gè)小時(shí)的車(chē)程對(duì)她來(lái)說(shuō)算不了什么!要去見(jiàn)親愛(ài)的人兒,路程...
    bony閱讀 243評(píng)論 0 0
  • 任之行于英彬日精進(jìn)分享 顯現(xiàn)-練習(xí)——使用 我行,一切行,我行任之行 選好人,做對(duì)事 懂人事 體驗(yàn)價(jià)值從滿足身...
    于英彬閱讀 258評(píng)論 0 0

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