Java程式設計師面試中的多執行緒問題

0、java中多執行緒同步是什麼?

在多執行緒程式下,同步能控制對共享資源的訪問。如果沒有同步,當一個java執行緒在修改一個共享變數時,另外一個執行緒正在使用或者更新同一個變數,這樣容易導致程式出現錯誤的結果。

1、解釋實現多執行緒的幾種方法?

一java執行緒可以實現runnable接口或者繼承thread類來實現,當你打算多重繼承時,優先選擇實現runnable。

2、thread.start()與thread.run()有什麼區別?

thread.start()方法(native)啟動執行緒,使之進入就緒狀態,當cpu分配時間該執行緒時,由jvm調度執行run()方法。

3、為什麼需要run()和start()方法,我們可以只用run()方法來完成任務嗎?

我們需要run()&start()這兩個方法是因為jvm創建一個單獨的執行緒不同於普通方法的調用,所以這項工作由執行緒的start方法來完成,start由本地方法實現,需要顯示地被調用,使用這倆個方法的另外一個好處是任何一個對象都可以作為執行緒運行,只要實現了runnable接口,這就避免因繼承了thread類而造成的java的多繼承問題。

4、什麼是threadlocal類,怎么使用它?

threadlocal是一個執行緒級別的局部變數,並非“本地執行緒”。threadlocal為每個使用該變數的執行緒提供了一個獨立的變數副本,每個執行緒修改副本時不影響其它執行緒對象的副本(譯者注)。

下面是執行緒局部變數(threadlocal variables)的關鍵點:

一個執行緒局部變數(threadlocal variables)為每個執行緒方便地提供了一個單獨的變數。

threadlocal實例通常作為靜態的私有的(private static)欄位出現在一個類中,這個類用來關聯一個執行緒。

當多個執行緒訪問threadlocal實例時,每個執行緒維護threadlocal提供的獨立的變數副本。

常用的使用可在dao模式中見到,當dao類作為一個單例類時,資料庫連結(connection)被每一個執行緒獨立的維護,互不影響。(基於執行緒的單例)

5、什麼時候拋出invalidmonitorstateexception異常,為什麼?

調用wait()/notify()/notifyall()中的任何一個方法時,如果當前執行緒沒有獲得該對象的鎖,那么就會拋出illegalmonitorstateexception的異常(也就是說程式在沒有執行對象的任何同步塊或者同步方法時,仍然嘗試調用wait()/notify()/notifyall()時)。由於該異常是runtimeexcpetion的子類,所以該異常不一定要捕獲(儘管你可以捕獲只要你願意).作為runtimeexception,此類異常不會在wait(),notify(),notifyall()的方法簽名提及。

6、sleep()、suspend()和wait()之間有什麼區別?

thread.sleep()使當前執行緒在指定的時間處於“非運行”(not runnable)狀態。執行緒一直持有對象的監視器。比如一個執行緒當前在一個同步塊或同步方法中,其它執行緒不能進入該塊或方法中。如果另一執行緒調用了 interrupt()方法,它將喚醒那個“睡眠的”執行緒。

注意:sleep()是一個靜態方法。這意味著只對當前執行緒有效,一個常見的錯誤是調用t.sleep(),(這裡的t是一個不同於當前執行緒的執行緒)。即便是執行t.sleep(),也是當前執行緒進入睡眠,而不是t執行緒。t.suspend()是過時的方法,使用suspend()導致執行緒進入停滯狀態,該執行緒會一直持有對象的監視器,suspend()容易引起死鎖問題。

object.wait()使當前執行緒出於“不可運行”狀態,和sleep()不同的是wait是object的方法而不是thread。調用 object.wait()時,執行緒先要獲取這個對象的對象鎖,當前執行緒必須在鎖對象保持同步,把當前執行緒添加到等待佇列中,隨後另一執行緒可以同步同一個對象鎖來調用object.notify(),這樣將喚醒原來等待中的執行緒,然後釋放該鎖。基本上wait()/notify()與sleep() /interrupt()類似,只是前者需要獲取對象鎖。

7、在靜態方法上使用同步時會發生什麼事?

同步靜態方法時會獲取該類的“class”對象,所以當一個執行緒進入同步的靜態方法中時,執行緒監視器獲取類本身的對象鎖,其它執行緒不能進入這個類的任何靜態同步方法。它不像實例方法,因為多個執行緒可以同時訪問不同實例同步實例方法。

8、當一個同步方法已經執行,執行緒能夠調用對象上的非同步實例方法嗎?

可以,一個非同步方法總是可以被調用而不會有任何問題。實際上,java沒有為非同步方法做任何檢查,鎖對象僅僅在同步方法或者同步代碼塊中檢查。如果一個方法沒有聲明為同步,即使你在使用共享數據java照樣會調用,而不會做檢查是否安全,所以在這種情況下要特別小心。一個方法是否聲明為同步取決於臨界區訪問(critial section access),如果方法不訪問臨界區(共享資源或者數據結構)就沒必要聲明為同步的。

下面有一個示例說明:common類有兩個方法synchronizedmethod1()和method1(),mythread類在獨立的執行緒中調用這兩個方法。

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();

}

}

這裡是程式的輸出:

running threadmythread-1

synchronizedmethod1 called

running threadmythread-2

method 1 called

synchronizedmethod1 done

method 1 done

結果表明即使synchronizedmethod1()方法執行了,method1()也會被調用。

9、 在一個對象上兩個執行緒可以調用兩個不同的同步實例方法嗎?

不能,因為一個對象已經同步了實例方法,執行緒獲取了對象的對象鎖。所以只有執行完該方法釋放對象鎖後才能執行其它同步方法。看下面代碼示例非常清晰:common 類 有synchronizedmethod1()和synchronizedmethod2()方法,mythread調用這兩個方法。

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、 什麼是死鎖

死鎖就是兩個或兩個以上的執行緒被無限的阻塞,執行緒之間相互等待所需資源。這種情況可能發生在當兩個執行緒嘗試獲取其它資源的鎖,而每個執行緒又陷入無限等待其它資源鎖的釋放,除非一個用戶進程被終止。就javaapi而言,執行緒死鎖可能發生在一下情況。

●當兩個執行緒相互調用thread.join()

●當兩個執行緒使用嵌套的同步塊,一個執行緒占用了另外一個執行緒必需的鎖,互相等待時被阻塞就有可能出現死鎖。

11、什麼是執行緒餓死,什麼是活鎖?

執行緒餓死和活鎖雖然不想是死鎖一樣的常見問題,但是對於並發編程的設計者來說就像一次邂逅一樣。

當所有執行緒阻塞,或者由於需要的資源無效而不能處理,不存在非阻塞執行緒使資源可用。javaapi中執行緒活鎖可能發生在以下情形:

●當所有執行緒在程式中執行object.wait(0),參數為0的wait方法。程式將發生活鎖直到在相應的對象上有執行緒調用object.notify()或者object.notifyall()。

●當所有執行緒卡在無限循環中。