對於Java多線程的常見問題
最近在準備工作面試的問題,所以找了很多的資料,和自己整理了相關可能會考到的。每天爭取發一篇。
1.多線程
實現方法:
一、繼承Thread,重寫run方法,調用start即可。
Class Thread1 extends Thread{
Public void run(){
//添加代碼
}
}
Public static void main(String[] args){
Thread1 st = new Thread1();
St.start();
}
二、實現runnable接口,重寫run方法,調用start。
Class RunThrad implements runnable{
Public void run(){
//添加代碼
}
}
RunThread t = new RunThread();
Thread th = new Thread(t);
th.start();
三、實現Callable接口類,並重寫call方法,使用FutureTask類來包裝Callable實現類的對象,並且使用FutureTask對象來作為Thread對象的target來創建線程。
class Mycallable implements Callable<Integer>{
Public int call(){
//添加代碼
}
}
public static void main(String[] args){
Mycallable<Integer> mycall = new Mycallable<Integer>();
FutureTask<Integer> ft = new FutureTask<Integer>(mycall);
Thread th = new Thread(ft);
th.start();
}
實現runnable和callable比繼承Thread方法的優勢:
1、避免單繼承的問題。
2、線程池只能夠放入runnable和callable類的線程。
線程主要有五種狀態,如下:
接下來介紹sleep,wait,notify,notifyAll,join,yield六種方法
對於wait,notify,notifyAll這三個,屬於object類的方法,但必須搭配synchronized同步塊來使用,即在synchronized修飾的同步代碼塊中或者方法中調用wait或者notify/notifyAll:
由於wait,notify/notifyAll是放在同步代碼塊中的,所以線程在執行的時候,肯定是進入了臨界狀態的,即該線程肯定是獲得了鎖的。
當執行wait方法時,會把當前的鎖釋放掉,讓出cpu,進入等待狀態。
當執行notify/notifyAll方法時,喚醒一個等待該對象鎖的線程,然後繼續往下執行,直到執行完synchronized的代碼後,再將鎖釋放。(註意,notify/notifyAll執行後,並不立即釋放鎖,而是需要等待執行完synchronized的代碼後)
如果在線程A,線程B中,在線程A中,執行wait,在線程B中執行notify,如果線程B先執行了notify,然後就結束了,然後線程A才去執行wait,那麽此時線程A就無法被喚醒了。舉例如下:有3個線程A,B,C。
class T implements Runnable{
public String i;
public String index;
public T(String i,String index){
this.i = i;
this.index = index;
}
@Override
public void run() {
synchronized (index) {
while (true) {
try {
index.notifyAll();
System.out.println(i);
index.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
public class demo {
public static void main(String[] args){
String string1 = "s1";
String string2 = "s2";
String string3 = "s3";
String index = "test";
T a1 = new T(string1,index);
T a2 = new T(string2,index);
T a3 = new T(string3,index);
Thread thread1 = new Thread(a1);
Thread thread2 = new Thread(a2);
Thread thread3 = new Thread(a3);
thread1.start();
thread2.start();
thread3.start();
}
}
輸出結果:
s2
s1
s3
s1
s2
s1
s3
s1
……
多線程中測試某個條件的變化用 if 還是用 while?
以前一直不明白 當在線程的run()方法中需要測試某個條件時,為什麽用while,而不用if,直到看到了這個簡單的例子,終於明白了。。。。
這個例子是這樣的:
有兩個線程從List中刪除數據,而只有一個線程向List中添加數據。初始時,List為空,只有往List中添加了數據之後,才能刪除List中的數據。添加數據的線程向List添加完數據後,調用notifyAll(),喚醒了兩個刪除線程,但是它只添加了一個數據,而現在有兩個喚醒的刪除線程,這時怎麽辦??
如果用 if 測試List中的數據的個數,則會出現IndexOutofBoundException,越界異常。原因是,List中只有一個數據,第一個刪除線程把數據刪除後,第二個線程再去執行刪除操作時,刪除失敗,從而拋出 IndexOutofBoundException。
但是如果用while 測試List中數據的個數,則不會出現越界異常!!!神奇。
當wait等待的條件發生變化時,會造成程序的邏輯混亂---即,List中沒有數據了,再還是有線程去執行刪除數據的操作。因此,需要用while循環來判斷條件的變化,而不是用if。
兩個線程之間的通信,可以采用如下兩種方式,一種是基於while輪詢,另外一種就是wait/notify的方式。
基於while輪詢的,B線程一直 一直循環條件,當符合條件時,則拋出異常,結束線程。
基於wait/notify的方式,則使用共享變量。當A線程放棄CPU使用權,進入阻塞狀態,則B線程獲得共享變量,開始執行,執行完畢後,B線程結束,放棄CPU使用權,A線程繼續。容易出現的問題是,A線程尚未執行,B線程先執行,打亂執行順序。則邏輯錯誤。
參考案例在:http://www.cnblogs.com/hapjin/p/5492619.html
sleep:
sleep讓當前線程休眠指定的時間。休眠完成後,狀態轉到就緒狀態。
yield: yield是放棄當前CPU資源,將CPU資源讓給其他線程去使用,但放棄的時間不確定。
join:
大部分情況下,主線程啟動了子線程,如果子線程需要完成大量復雜的運算,則主線程會先於子線程結束。但主線程如果需要在子線程運行完畢後使用子線程的結果,則必須在主線程中使用join,這樣主線程會等待子線程結束,然後主線程再結束。
方法join,是使得所屬線程對象x正常執行完run方法,而使當前線程z進行無限制阻塞,直到對象x結束後,再繼續執行z後面的代碼。
public class demo
{
public static void main(String[] args) throws InterruptedException
{
Thread producer = new Producer();
producer.start();
producer.join();
System.out.println("main end");
}
}
class Producer extends Thread
{
public void run()
{
for (int i = 0; i < 100000; i++)
System.out.println("producer end");
}
}
在以上案例中,當把producer.join()註釋掉後,會先打印main end,再打印producer end。因為子線程運算復雜,所以主線程會先結束。而如果加上這一句,則主線程會等待producer線程的結束,主線程再銷毀。也就是說,哪一個線程被調用,則必須等待該線程結束,調用的主線程才能繼續執行下一步。
對於Java多線程的常見問題