多線程Multi-Threading

0、Java中多線程同步是什麼?java

在多線程程序下,同步能控制對共享資源的訪問。若是沒有同步,當一個Java線程在修改一個共享變量時,另一個線程正在使用或者更新同一個變量,這樣容易致使程序出現錯誤的結果。有兩種方式,分別是:同步代碼塊和同步方法;數據庫

一、解釋實現多線程的幾種方法?編程

一Java線程能夠實現Runnable接口或者繼承Thread類來實現,當你打算多重繼承時,優先選擇實現Runnable。 擴展一種:實現Callable接口。這個得和線程池結合。安全

二、Thread.start()與Thread.run()有什麼區別?數據結構

run():封裝了被線程執行的代碼,直接調用僅僅是普通方法的調用多線程

start():啓動線程,並由JVM自動調用run()方法併發

Thread.start()方法(native)啓動線程,使之進入就緒狀態,當cpu分配時間該線程時,由JVM調度執行run()方法。this

三、爲何須要run()和start()方法,咱們能夠只用run()方法來完成任務嗎?spa

咱們須要run()&start()這兩個方法是由於JVM建立一個單獨的線程不一樣於普通方法的調用,因此這項工做由線程的start方法來完成,start由本地方法實現,須要顯示地被調用,使用這倆個方法的另一個好處是任何一個對象均可以做爲線程運行,只要實現了Runnable接口,這就避免因繼承了Thread類而形成的Java的多繼承問題。線程

四、什麼是ThreadLocal類,怎麼使用它?

ThreadLocal是一個線程級別的局部變量,並不是「本地線程」。ThreadLocal爲每一個使用該變量的線程提供了一個獨立的變量副本,每一個線程修改副本時不影響其它線程對象的副本。

下面是線程局部變量(ThreadLocal variables)的關鍵點:

一個線程局部變量(ThreadLocal variables)爲每一個線程方便地提供了一個單獨的變量。

ThreadLocal實例一般做爲靜態的私有的(private static)字段出如今一個類中,這個類用來關聯一個線程。

當多個線程訪問ThreadLocal實例時,每一個線程維護ThreadLocal提供的獨立的變量副本。

經常使用的使用可在DAO模式中見到,當DAO類做爲一個單例類時,數據庫連接(connection)被每個線程獨立的維護,互不影響。(基於線程的單例) 

五、何時拋出InvalidMonitorStateException異常,爲何?

調用wait()/notify()/notifyAll()中的任何一個方法時,若是當前線程沒有得到該對象的鎖,那麼就會拋出IllegalMonitorStateException的異常(也就是說程序在沒有執行對象的任何同步塊或者同步方法時,仍然嘗試調用wait()/notify()/notifyAll()時)。因爲該異常是RuntimeExcpetion的子類,因此該異常不必定要捕獲(儘管你能夠捕獲只要你願意).做爲RuntimeException,此類異常不會在wait(),notify(),notifyAll()的方法簽名說起。

六、Sleep()、suspend()和wait()之間有什麼區別?

sleep():必須指時間;不釋放鎖。

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()相似,只是前者須要獲取對象鎖。

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

同步靜態方法時會獲取該類的「Class」對象,因此當一個線程進入同步的靜態方法中時,線程監視器獲取類自己的對象鎖,其它線程不能進入這個類的任何靜態同步方法。它不像實例方法,由於多個線程能夠同時訪問不一樣實例同步實例方法。

八、當一個同步方法已經執行,線程可以調用對象上的非同步實例方法嗎?

能夠,一個非同步方法老是能夠被調用而不會有任何問題。實際上,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()也會被調用

 

九、 在一個對象上兩個線程能夠調用兩個不一樣的同步實例方法嗎?

不能,由於一個對象已經同步了實例方法,線程獲取了對象的對象鎖。因此只有執行完該方法釋放對象鎖後才能執行其它同步方法。看下面代碼示例很是清晰: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();

}

}

十、 什麼是死鎖

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

 

●當兩個線程相互調用Thread.join()

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

十一、什麼是線程餓死,什麼是活鎖?

線程餓死和活鎖雖然不想是死鎖同樣的常見問題,可是對於併發編程的設計者來講就像一次邂逅同樣。

當全部線程阻塞,或者因爲須要的資源無效而不能處理,不存在非阻塞線程使資源可用。JavaAPI中線程活鎖可能發生在如下情形:

●當全部線程在程序中執行Object.wait(0),參數爲0的wait方法。程序將發生活鎖直到在相應的對象上有線程調用Object.notify()或者Object.notifyAll()。

●當全部線程卡在無限循環中。

十二、爲何wait(),notify(),notifyAll()等方法都定義在Object類中
    由於這些方法的調用是依賴於鎖對象的,而同步代碼塊的鎖對象是任意鎖。
    而Object代碼任意的對象,因此,定義在這裏面。

1三、線程的生命週期圖     新建 -- 就緒 -- 運行 -- 死亡     新建 -- 就緒 -- 運行 -- 阻塞 -- 就緒 -- 運行 -- 死亡

相關文章
相關標籤/搜索