JAVA多線程 問題 轉載

參考:http://ifeve.com/java-multi-threading-concurrency-interview-questions-with-answers/html

http://www.cnblogs.com/dolphin0520/p/3958019.htmljava

1.Java 中多線程同步是什麼?編程

       在多線程程序下,同步能實現控制對共享資源的訪問。若是沒有同步,當一個 Java 線程在修改一個共享變量時,另一個線程正在使用或者更新同一個變量,這樣容易致使程序出現錯誤的結果。安全

2.解釋實現多線程的幾種方法?數據結構

   1>  Java 線程能夠實現 Runnable 接口:(當你打算多重繼承時,優先選擇實現 Runnable)多線程

[java] 
 
  1. class Foo implements Runnable {  
  2.         public void run(){  
  3.             //...  
  4.         }  
  5.     }  
  6.     Thread t = new Thread(new Foo());  
  7.     t.start();  

 

    2> 繼承 Thread 類來實現:併發

[java] 
 
  1. class MyThread extends Thread {  
  2.     public void run() {  
  3.     ……   }  
  4. }  

 

三、Thread.start ()與 Thread.run ()有什麼區別?this

       Thread.start ()方法啓動線程,使之進入就緒狀態,當 cpu 分配時間該線程時,由 JVM 調度執行 run ()方法。start()並不表明線程當即能夠執行了。spa

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

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

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

        該類提供了線程局部變量。這些變量不一樣於它們的普通對應物,由於訪問一個變量(經過其 get 或 set 方法)的每一個線程都有本身的局部變量,它獨立於變量的初始化副本。ThreadLocal 實例一般是類中的私有靜態字段,它們但願將狀態與某一個線程(例如,用戶 ID 或事務 ID)相關聯。

      例如,在下面的類中,私有靜態 ThreadLocal 實例(serialNum)爲調用該類的靜態 SerialNum.get() 方法的每一個線程維護了一個「序列號」,該方法將返回當前線程的序列號。(線程的序列號是在第一次調用 SerialNum.get() 時分配的,並在後續調用中不會更改。)

[java] 
 
  1. public class SerialNum {  
  2.      // The next serial number to be assigned  
  3.      private static int nextSerialNum = 0;  
  4.   
  5.      private static ThreadLocal serialNum = new ThreadLocal() {  
  6.          protected synchronized Object initialValue() {  
  7.              return new Integer(nextSerialNum++);  
  8.          }  
  9.      };  
  10.   
  11.      public static int get() {  
  12.          return ((Integer) (serialNum.get())).intValue();  
  13.      }  
  14.  }  

        每一個線程都保持一個對其線程局部變量副本的隱式引用,只要線程是活動的而且 ThreadLocal 實例是可訪問的;在線程消失以後,其線程局部實例的全部副本都會被垃圾回收(除非存在對這些副本的其餘引用)。

六、何時拋出 IillegalMonitorStateException 異常,爲何?

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

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

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

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

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

      能夠,一個非同步方法老是能夠被調用而不會有任何問題。實際上,Java 沒有爲非同步方法作任何檢查,鎖對象僅僅在同步方法或者同步代碼塊中檢查。若是一個方法沒有聲明爲同步,即便你在使用共享數據 Java 照樣會調用,而不會作檢查是否安全,因此在這種狀況下要特別當心。一個方法是否聲明爲同步取決於臨界區訪問(critial section access),若是方法不訪問臨界區(共享資源或者數據結構)就不必聲明爲同步的。

下面有一個示例說明:Common 類有兩個方法 synchronizedMethod1()和 method1(),MyThread 類在獨立的線程中調用這兩個方法。

[java]
 
  1. public class Common {   
  2.   
  3.        public synchronized void synchronizedMethod1() {  
  4.             System.out.println (「synchronizedMethod1 called」);  
  5.             try {  
  6.               Thread.sleep (1000);  
  7.             } catch (InterruptedException e) {  
  8.                 e.printStackTrace ();  
  9.             }  
  10.             System.out.println (「synchronizedMethod1 done」);  
  11.        }  
  12.       public void method1() {  
  13.            System.out.println (「Method 1 called」);  
  14.            try {  
  15.                Thread.sleep (1000);  
  16.            } catch (InterruptedException e) {  
  17.                e.printStackTrace ();  
  18.            }  
  19.                System.out.println (「Method 1 done」);  
  20.      }  
  21. }  
  22. public class MyThread extends Thread {  
  23.        private int id = 0;  
  24.        private Common common;   
  25.   
  26.       public MyThread (String name, int no, Common object) {  
  27.            super(name);  
  28.            common = object;  
  29.            id = no;  
  30.       }   
  31.   
  32.       public void run () {  
  33.           System.out.println (「Running Thread」 + this.getName ());  
  34.           try {  
  35.             if (id == 0) {  
  36.                  common.synchronizedMethod1();  
  37.             } else {  
  38.                 common.method1();  
  39.             }  
  40.           } catch (Exception e) {  
  41.               e.printStackTrace ();  
  42.       }  
  43. }   
  44.   
  45. public static void main (String[] args) {  
  46.       Common c = new Common ();  
  47.       MyThread t1 = new MyThread (「MyThread-1″, 0, c);  
  48.       MyThread t2 = new MyThread (「MyThread-2″, 1, c);  
  49.       t1.start ();  
  50.       t2.start ();  
  51.   }  
  52. }  

這裏是程序的輸出:

Running ThreadMyThread-1
synchronizedMethod1 called
Running ThreadMyThread-2
Method 1 called
synchronizedMethod1 done
Method 1 done

結果代表即便 synchronizedMethod1()方法執行了,method1()也會被調用。

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

不能,由於一個對象已經同步了實例方法,線程獲取了對象的對象鎖。因此只有執行完該方法釋放對象鎖後才能執行其它同步方法。看下面代碼示例很是清晰:Common 類有 synchronizedMethod1()和 synchronizedMethod2()方法,MyThread 調用這兩個方法。

[java]
 
  1. public class Common {  
  2. public synchronized void synchronizedMethod1() {  
  3. System.out.println (「synchronizedMethod1 called」);  
  4. try {  
  5. Thread.sleep (1000);  
  6. catch (InterruptedException e) {  
  7. e.printStackTrace ();  
  8. }  
  9. System.out.println (「synchronizedMethod1 done」);  
  10. }   
  11.   
  12. public synchronized void synchronizedMethod2() {  
  13. System.out.println (「synchronizedMethod2 called」);  
  14. try {  
  15. Thread.sleep (1000);  
  16. catch (InterruptedException e) {  
  17. e.printStackTrace ();  
  18. }  
  19. System.out.println (「synchronizedMethod2 done」);  
  20. }  
  21. }  
  22. public class MyThread extends Thread {  
  23. private int id = 0;  
  24. private Common common;   
  25.   
  26. public MyThread (String name, int no, Common object) {  
  27. super(name);  
  28. common = object;  
  29. id = no;  
  30. }   
  31.   
  32. public void run () {  
  33. System.out.println (「Running Thread」 + this.getName ());  
  34. try {  
  35. if (id == 0) {  
  36. common.synchronizedMethod1();  
  37. else {  
  38. common.synchronizedMethod2();  
  39. }  
  40. catch (Exception e) {  
  41. e.printStackTrace ();  
  42. }  
  43. }   
  44.   
  45. public static void main (String[] args) {  
  46. Common c = new Common ();  
  47. MyThread t1 = new MyThread (「MyThread-1″, 0, c);  
  48. MyThread t2 = new MyThread (「MyThread-2″, 1, c);  
  49. t1.start ();  
  50. t2.start ();  
  51. }  
  52. }  


十一、什麼是死鎖

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

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

 

 如何避免死鎖?

  Java多線程中的死鎖 死鎖是指兩個或兩個以上的進程在執行過程當中,因爭奪資源而形成的一種互相等待的現象,若無外力做用,它們都將沒法推動下去。這是一個嚴重的問題,由於死鎖會讓你的程序掛起沒法完成任務,死鎖的發生必須知足如下四個條件:

  • 互斥條件:一個資源每次只能被一個進程使用。
  • 請求與保持條件:一個進程因請求資源而阻塞時,對已得到的資源保持不放。
  • 不剝奪條件:進程已得到的資源,在末使用完以前,不能強行剝奪。
  • 循環等待條件:若干進程之間造成一種頭尾相接的循環等待資源關係。


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

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

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

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

相關文章
相關標籤/搜索