面試問題:線程控制和線程之間的通信


一、面試碰到過這樣的問題:

     讓兩個多線程依次打印奇數和偶數。

二、問題解釋:

     大家都知道,線程一旦啟動,CPU有一定的調度策略,不是人為能隨便控制的。這樣的題目在實際工程中也沒有多大的意義,不用多線程,直接用方法依次調用就可以了,但為什么還要考這樣的題目,實際上是考對多線程和線程之間的通信相關知識點。現在總結如下:

 

三、線程控制和線程之間的通信

   主要是三個方法wait(); notify(); notifyAll();

   1、wait()、notify/notifyAll() 方法是Object的本地final方法,無法被重寫。

   2、wait()使當前線程阻塞,前提是 必須先獲得鎖,一般配合synchronized 關鍵字使用,即,一般在synchronized 同步代碼塊里使用 wait()、notify/notifyAll() 方法。
   3、 由於 wait()、notify/notifyAll() 在synchronized 代碼塊執行,說明當前線程一定是獲取了鎖的。
       當線程執行wait()方法時候,會釋放當前的鎖,然后讓出CPU,進入等待狀態。
       只有當 notify/notifyAll() 被執行時候,才會喚醒一個或多個正處於等待狀態的線程,然后繼續往下執行,直到執行完synchronized 代碼塊的代碼或是中途遇到wait() ,再次釋放鎖。
       也就是說,notify/notifyAll() 的執行只是喚醒沉睡的線程,而不會立即釋放鎖,鎖的釋放要看代碼塊的具體執行情況。所以在編程中,盡量在使用了notify/notifyAll() 后立即退出臨界區,以喚醒其他線程 
   4、wait() 需要被try catch包圍,中斷也可以使wait等待的線程喚醒。
   5、notify 和wait 的順序不能錯,如果A線程先執行notify方法,B線程在執行wait方法,那么B線程是無法被喚醒的。
   6、notify 和 notifyAll的區別
       notify方法只喚醒一個等待(對象的)線程並使該線程開始執行。所以如果有多個線程等待一個對象,這個方法只會喚醒其中一個線程,選擇哪個線程取決於操作系統對多線程管理的實現。notifyAll 會喚醒所有等待(對象的)線程,盡管哪一個線程將會第一個處理取決於操作系統的實現。如果當前情況下有多個線程需要被喚醒,推薦使用notifyAll 方法。比如在生產者-消費者里面的使用,每次都需要喚醒所有的消費者或是生產者,以判斷程序是否可以繼續往下執行。
  7、wait(), notify(), notifyAll() 必須是同一對象的多個線程之間的通信
 
四、面試題舉例
     兩個線程,依次執行打印奇數和偶數。
    
     因為wait() notifyAll() 必須是同一對象的多個線程之間的通信,所以有如下程序:
     
 1 public class Printer {
 2     
 3     int i = 1;
 4 
 5     synchronized public void  printEven(){
 6         for(;i<=100;) {
 7             if (i % 2 == 1) {
 8                 try {
 9                     wait();
10                 } catch (InterruptedException e) {
11                     e.printStackTrace();
12                 }
13             } else {
14                 System.out.println(Thread.currentThread().getName() + " is printting i: " + i++);
15                 notifyAll();
16             }
17         }
18     }
19 
20     synchronized public void printOdd(){
21         for(; i<=100; ) {
22             if (i % 2 == 0) {
23                 try {
24                     wait();
25                 } catch (InterruptedException e) {
26                     e.printStackTrace();
27                 }
28             } else {
29                 System.out.println(Thread.currentThread().getName() + " is printting i: " + i++);
30                 notifyAll();
31             }
32         }
33     }
34 
35 }

 

要實現兩個線程分別打印,所以有如下代碼:

 1 public class PrintEven implements Runnable{
 2     Printer printer;
 3     public PrintEven(Printer printer){
 4         this.printer = printer;
 5     }
 6     public void run() {
 7         printer.printEven();
 8 
 9     }
10 }
 1 public class PrintOdd implements Runnable {
 2     Printer printer;
 3     public PrintOdd(Printer printer){
 4         this.printer = printer;
 5     }
 6     public void run() {
 7         printer.printOdd();
 8 
 9     }
10 }

 

主函數: 把printer作為對象傳入

 1     public  static void main(String[] args){
 2 
 3         Printer printer = new Printer();
 4 
 5         Runnable printOdd = new PrintOdd(printer);
 6         Thread threadOdd = new Thread(printOdd, "thread1");
 7 
 8         Runnable printEven = new PrintEven(printer);
 9         Thread threadEven = new Thread(printEven, "thread2");
10 
11         threadOdd.start();
12         threadEven.start();
13     }

程序運行結果:

 1 thread1 is printting i: 1
 2 thread2 is printting i: 2
 3 thread1 is printting i: 3
 4 thread2 is printting i: 4
 5 thread1 is printting i: 5
 6 thread2 is printting i: 6
 7 thread1 is printting i: 7
 8 thread2 is printting i: 8
 9 thread1 is printting i: 9
10 thread2 is printting i: 10
11 thread1 is printting i: 11
12 thread2 is printting i: 12
13 thread1 is printting i: 13
14 thread2 is printting i: 14
15 thread1 is printting i: 15
16 thread2 is printting i: 16
17 thread1 is printting i: 17
18 thread2 is printting i: 18
19 thread1 is printting i: 19
20 thread2 is printting i: 20

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM