Java多線程總結

1、多線程有哪幾種實現方法?舉個例子說明下線程的同步。

(1)Java多線程有兩種實現方式:繼承Thread類和實現Runnable接口,Thread就是實現了Runnable接口。

兩個最簡單的線程例子:

  1. package chc.runnable;  
  2.   
  3. public class ThreadTest2 {  
  4.     public static void main(String[] args) throws InterruptedException {  
  5.         Thread1 t = new Thread1();  
  6.         //t.run(); //這裏也不能直接調用方法  
  7.         t.start();  
  8.         for (int i = 0; i < 100; i++) {  
  9.             System.out.println("main:"+i);  
  10.         }  
  11.     }  
  12. }  
  13.   
  14. class Thread1 extends Thread{  
  15.     public void run() {  
  16.         for (int i = 0; i < 100; i++) {  
  17.             System.out.println("Thread-----:"+i);  
  18.         }  
  19.     }  
  20. }  
  1. package chc.runnable;  
  2.   
  3. public class ThreadTest1 {  
  4.     public static void main(String[] args) {  
  5.         Runnable1 r =new Runnable1();  
  6.         Thread t1=new Thread(r);  
  7.         t1.start();  
  8.     }  
  9. }  
  10.   
  11. class Runnable1  implements Runnable {  
  12.   
  13.     public void run() {  
  14.         // TODO Auto-generated method stub  
  15.         for (int i = 1; i <= 5; i++) {  
  16.             System.out.println("實現Runnable接口的線程----->"+i);  
  17.         }  
  18.     }  
  19.   
  20. }  

通過上兩個例子發現,當啓動線程的時候並不影響主程序的繼續執行。

(2)線程同步問題

使用synchronnized關鍵字

銀行賬戶存取錢的問題:

  1. public class ThreadTest {  
  2.     public static void main(String[] args){  
  3.         ThreadA  t1=new ThreadA();  
  4.         t1.start();  
  5.         ThreadB  t2=new ThreadB();  
  6.         t2.start();  
  7.     }  
  8. }  
  9.   
  10. class ThreadA extends Thread{  
  11.     @Override  
  12.     public void run(){  
  13.         for(int i=0;i<100;i++){  
  14.             account.add();  
  15.             try {  
  16.                 sleep(10);//模擬銀行系統處理時間  
  17.             } catch (InterruptedException e) {  
  18.                 // TODO Auto-generated catch block  
  19.                 e.printStackTrace();  
  20.             }  
  21.         }  
  22.     }  
  23. }  
  24.   
  25. class ThreadB extends Thread{  
  26.     @Override  
  27.     public void run() {  
  28.         for(int i=0;i<100;i++){  
  29.             account.remove();  
  30.             try {  
  31.                 sleep(10);<span style="font-family: SimSun;">//模擬銀行系統處理時間</span>  
  32.             } catch (InterruptedException e) {  
  33.                 // TODO Auto-generated catch block  
  34.                 e.printStackTrace();  
  35.             }  
  36.         }  
  37.     }  
  38. }  
  39.   
  40. class account {  
  41.     public static   int count=1000;  
  42.       
  43.     //減去100元  
  44.     public static synchronized void remove(){  
  45.         count=count-100;  
  46.         System.out.println("減去100元,卡內餘額"+count);  
  47.     }  
  48.       
  49.     //增加100元  
  50.     public static synchronized void add(){  
  51.         count=count+100;  
  52.         System.out.println("加上100元,卡內餘額"+count);  
  53.     }  
  54. }  
注意:上例中將account類的add()和remove()方法都加上了static關鍵字,因爲這樣在線程中可以直接通過類名調用到這兩個方法,而不需要實例化對象。

因爲synchronized同步只對同一個對象中的方法有效,也就是說一個線程正在執行account的add()方法,另一個線程是可以調用到另一個對象的add方法的。

2、啓動一個線程是用run()還是start(),調用的時候有什麼區別?

當然是start()了,當調用線程的start()方法的時候,線程就會進入到就緒狀態

run()方法是線程的執行入口,當線程從就緒狀態進入到執行狀態時首先要從run()方法開始執行。

當然,我們也是可以直接通過線程對象調用該對象的run()方法的,只是這只是一次普通的調用,並沒有啓動任何一個線程。

當我們調用start()方法時,是另外啓動了一個線程去執行線程類的代碼,並不影響主程序的執行,但是調用run()方法的時候要等待run()方法內的代碼執

行完主程序才可以向下執行,舉個例子:

  1. public class ThreadDemo2 {  
  2.     public static void main(String[] args) {  
  3.         Thread5 t1=new Thread5();  
  4.         Thread6 t2=new Thread6();  
  5.         t1.run();  
  6.         t2.start();  
  7.         for(int i=0;i<100;i++){  
  8.             System.out.println("主進程執行:"+i);  
  9.         }  
  10.     }  
  11. }  
  12.   
  13. class Thread5 extends Thread{  
  14.     @Override  
  15.     public void run() {  
  16.         for(int i=0;i<100;i++){  
  17.             System.out.println("Thread5執行:"+i);  
  18.         }  
  19.     }  
  20. }  
  21.   
  22. class Thread6 extends Thread{  
  23.     @Override  
  24.     public void run() {  
  25.         for(int i=0;i<100;i++){  
  26.             System.out.println("Thread6執行:"+i);  
  27.         }  
  28.     }  
  29. }  
輸出結果的順序是:Thread5全部打印完後 Thread6和主程序交替打印。驗證了上面的說法

3、當一個線程進入到一個對象的synchronized方法,那麼其他線程是否可以進入該對象的其它方法?

不一定,看情況

如果其它方法加了static關鍵字,那麼該方法屬於類,不屬於對象,不能與對象的方法保持同步(即使有synchronized關鍵字),是能進入的。

如果其它方法不帶有static關鍵字且帶有synchronized關鍵字,那麼不能進入,如果不帶,則能。

再其次就看方法內部有沒有wait()方法釋放鎖了

4、子線程循環2次,接着主線程循環3次,接着子線程循環3次,接着主線程循環3次,如此循環5次,請寫出程序代碼。

  1. public class ThreadDemo5 {  
  2.     static boolean thread_flag=false;//指示子線程循環是否結束  
  3.     static boolean main_flag=true;   //調用子線程的start方法後變爲true,循環等待thread_flag爲true(也就是子線程循環完)的時刻,主線程循環完又變爲false;  
  4.     public static void main(String[] args) {  
  5.         for(int k=0;k<5;k++){  
  6.             Thread7 t=new Thread7();  
  7.             t.start();  
  8.             main_flag=true;  
  9.             while (main_flag) {//循環等待thread_f  
  10.                 if(thread_flag){  
  11.                     for(int i=0;i<3;i++){  
  12.                         System.out.println("主線程第一次循環"+(i+1)+"次");  
  13.                     }  
  14.                     thread_flag=false;  
  15.                     main_flag=false;  
  16.                 }     
  17.             }  
  18.         }  
  19.     }  
  20. }  
  21.   
  22. class Thread7 extends Thread {  
  23.     static boolean flag=true;//標誌子線程第幾次循環,當值爲true的時候子線程循環2次,否則循環3次  
  24.     public void run() {  
  25.         if(flag){  
  26.             for(int i=0;i<2;i++){  
  27.                 System.out.println("子線程第一次循環"+(i+1)+"次");  
  28.             }  
  29.             flag=false;  
  30.         }else{  
  31.             for(int i=0;i<3;i++){  
  32.                 System.out.println("子線程第二次循環"+(i+1)+"次");  
  33.             }  
  34.             flag=true;  
  35.         }  
  36.         ThreadDemo5.thread_flag=true;  
  37.           
  38.           
  39.     }  
  40. }  

這個題就是要注意調用子線程的start方法的時候並不能阻止主程序繼續向下執行,所以我們要用變量來標記。

5、sleep()和wait()有何異同?

(1)首先一個最明顯的區別是  wait是Object類的方法,而sleep()是Thread類的靜態方法,誰調用了該方法誰去休眠,即使在a線程裏調用了b線程的sleep方法,實際上還是a線程去休眠.

(2)比較重要的一點是sleep沒有釋放出鎖,而wait釋放了鎖,是其他線程可以使用同步塊資源。

     sleep不出讓系統資源;wait是進入線程等待池等待,出讓系統資源,其他線程可以佔用CPU。一般wait不會加時間限制,因爲如果wait線程的運行資源不夠,再出來也沒用,要

     等待其他線程調用notify/notifyAll喚醒等待池中的所有線程,纔會進入就緒隊列等待OS分配系統資源。sleep(milliseconds)可以用時間指定使它自動喚醒過來,如果時間不到

     只能調用interrupt()強行打斷。

(3)使用範圍:

     wait,notify和notifyAll只能在同步控制方法或者同步控制塊裏面使用,而sleep可以在任何地方使用 
     synchronized(x){ 
       x.notify() 
       //或者wait() 
      }

(4)sleep需要捕獲異常,而wait不需要。

6、現在有T1 T2 T3三個線程,怎樣保證T2在T1執行完之後執行 T3在T2執行完之後執行

這題主要是考察對join()方法的使用。

當線程A當中執行了線程B.join(),那麼A線程要等待B線程執行完纔可以執行。

  1. public class JoinDemo {  
  2.     public static void main(String[] args) {  
  3.         T1 t1=new T1("T1");  
  4.         T2 t2=new T2("T2");  
  5.         T3 t3=new T3("T3");  
  6.         t1.start();  
  7.         try {  
  8.             t1.join();  
  9.         } catch (InterruptedException e) {  
  10.             // TODO Auto-generated catch block  
  11.             e.printStackTrace();  
  12.         }  
  13.         t2.start();  
  14.         try {  
  15.             t2.join();  
  16.         } catch (InterruptedException e) {  
  17.             // TODO Auto-generated catch block  
  18.             e.printStackTrace();  
  19.         }  
  20.         t3.start();  
  21.     }  
  22. }  
  23.   
  24. class T1 extends  Thread{  
  25.     private String name;  
  26.     public T1(String name) {  
  27.         this.name=name;  
  28.     }  
  29.   
  30.     @Override  
  31.     public void run() {  
  32.         for(int i=0;i<5;i++){  
  33.             try {  
  34.                 sleep(5);  
  35.             } catch (InterruptedException e) {  
  36.                 // TODO Auto-generated catch block  
  37.                 e.printStackTrace();  
  38.             }  
  39.             System.out.println(this.name+"循環"+i);  
  40.         }  
  41.     }  
  42. }  
  43. class T2 extends  Thread{  
  44.     private String name;  
  45.     public T2(String name) {  
  46.         this.name=name;  
  47.     }  
  48.     @Override  
  49.     public void run() {  
  50.         for(int i=0;i<5;i++){  
  51.             try {  
  52.                 sleep(5);  
  53.             } catch (InterruptedException e) {  
  54.                 // TODO Auto-generated catch block  
  55.                 e.printStackTrace();  
  56.             }  
  57.             System.out.println(this.name+"循環"+i);  
  58.         }  
  59.     }  
  60. }  
  61. class T3 extends  Thread{  
  62.     private String name;  
  63.     public T3(String name) {  
  64.         this.name=name;  
  65.     }  
  66.     @Override  
  67.     public void run() {  
  68.         for(int i=0;i<5;i++){  
  69.             System.out.println(this.name+"循環"+i);  
  70.         }  
  71.     }  
  72. }  

7、簡述synchronized和java.util.concurrent.locks.Lock的異同?

主要相同點:Lock能完成synchronized所實現的所有功能

主要不同點:Lock有比synchronized更精確的線程語義和更好的性能。synchronized會自動釋放鎖,而Lock一定要求程序員手工釋放,並且必須在finally從句中釋放。

Lock還有更強大的功能,例如,它的tryLock方法可以非阻塞方式去拿鎖。

發佈了11 篇原創文章 · 獲贊 3 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章