2012-11-25 28 views
3

我需要兩個線程來寫入一個共享的整數數組。兩個線程都需要寫入該數組的所有元素。每個線程將寫入1或7,結果應該像171717171(或71717171)。爲此,我在位置0處寫入第一個Thread1,然後等待。線程2現在寫入位置0和1,通知線程1,並等待。 Thread1在位置1和2處寫入,通知Thread2並等待等等。使用下面的代碼,我可以得到正確的輸出,但是當與JPF一起運行時,它會發現死鎖。它變得非常令人沮喪,因爲我找不到它有什麼問題。任何意見,將不勝感激。線程調度 - 共享數組

import java.util.logging.Level; 
import java.util.logging.Logger; 


public class WriterThreadManager { 

    private int[] array = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 
    private Thread thread7; 
    private Thread thread1; 

    public static void main(String[] args) { 
     WriterThreadManager mng = new WriterThreadManager(); 
     mng.exec(); 

    } 

    public WriterThreadManager() { 
     thread7 = new Thread(new WriterRunnable(this, 7)); 
     thread1 = new Thread(new WriterRunnable(this, 1)); 
    } 

    public void overwriteArray(int pos, int num) { 
     array[pos] = num; 
     printArray(); 
    } 

    private void printArray() { 
     for (int i = 0; i < array.length; i++) { 
      System.out.print(array[i]); 
     } 
     System.out.println(""); 
    } 

    public synchronized void stopThread() { 
     try { 
      this.wait(); 
     } catch (InterruptedException ex) { 
      Logger.getLogger(WriterThreadManager.class.getName()).log(Level.SEVERE, null, ex); 
     } 
    } 

    public synchronized void wakeUpThread() { 
     notifyAll(); 
    } 

    private void exec() { 
     thread7.start(); 
     thread1.start(); 
    } 

    public int length() { 
     return array.length; 
    } 
} 



public class WriterRunnable implements Runnable { 

    private WriterThreadManager mng; 
    private int numberToWrite; 
    private static boolean flag = true; 

    @Override 
    public void run() { 
     int counter = 0; 
     int j = 0; 

     //first thread to get in should write only at 
     //position 0 and then wait. 
     synchronized (mng) { 
      if (flag) { 
       flag = false; 
       mng.overwriteArray(0, numberToWrite); 
       j = 1; 
       waitForOtherThread(); 
      } 
     } 
     for (int i = j; i < mng.length(); i++) { 
      mng.overwriteArray(i, numberToWrite); 
      counter++; 
      if (i == mng.length() - 1) { 
       mng.wakeUpThread(); 
       break; 
      } 
      if (counter == 2) { 
       waitForOtherThread(); 
       counter = 0; 
      } 
     } 
    } 

    private void waitForOtherThread() { 
     mng.wakeUpThread(); 
     mng.stopThread(); 
    } 

    public WriterRunnable(WriterThreadManager ar, int num) { 
     mng = ar; 
     numberToWrite = num; 
    } 
} 

PS:在執行的例子:

1000000000 
7000000000 
7700000000 
7100000000 
7110000000 
7170000000 
7177000000 
7171000000 
7171100000 
7171700000 
7171770000 
7171710000 
7171711000 
7171717000 
7171717700 
7171717100 
7171717110 
7171717170 
7171717177 
7171717171 

從JPF錯誤快照如下:

thread java.lang.Thread:{id:1,name:Thread-1,status:WAITING,priority:5,lockCount:1,suspendCount:0} 
    waiting on: [email protected] 
    call stack: 
    at java.lang.Object.wait(Object.java) 
    at WriterThreadManager.stopThread(WriterThreadManager.java:43) 
    at WriterRunnable.waitForOtherThread(WriterRunnable.java:53) 
    at WriterRunnable.run(WriterRunnable.java:45) 

thread java.lang.Thread:{id:2,name:Thread-2,status:WAITING,priority:5,lockCount:1,suspendCount:0} 
    waiting on: [email protected] 
    call stack: 
    at java.lang.Object.wait(Object.java) 
    at WriterThreadManager.stopThread(WriterThreadManager.java:43) 
    at WriterRunnable.waitForOtherThread(WriterRunnable.java:53) 
    at WriterRunnable.run(WriterRunnable.java:45) 
+1

是否需要嚴格依次寫入?這似乎是使用Semaphore的經典案例。 – Perception

+2

那麼,你在3個不同的對象上同步(管理器和兩個可運行的)。你不會在循環內部等待()以檢查條件。並且對數組的每次訪問都不同步。絕對使用更高級別的抽象(例如信號量)來同步你的線程。 –

+0

由於這是作業,OP可能會受限於他可以使用的內容。 –

回答

3

我相信比賽是由於這種方法:

private void waitForOtherThread() { 
    mng.wakeUpThread(); 
    mng.stopThread(); 
} 

雖然個別wakeUpThread()stopThread()方法是同步的,你有這些調用之間意想不到的線程調度的機會。

考慮:

thread7 - notify thread1 to wakup 
thread1 - wake up 
thread1 - work to completion 
thread1 - notify thread7 to wakeup 
thread1 - wait to be notified to wakeup 
thread7 - wait to be notified to wakeup 

在這種情況下,你已經陷入僵局,因爲線程1 thread7之前發送其notifyAll的()有機會等待()它。

在不同的環境中運行可能會混淆您的時間並導致出現這些類型的行爲。

爲了避免這種情況我建議這樣做:

private void waitForOtherThread() { 
    synchronized(mng) { 
     mng.wakeUpThread(); 
     mng.stopThread(); 
    } 
} 

或者更好的是,使用一個信號量,@KumarVivekMitra建議。信號量將通知系統和計數器結合起來,以便通知和等待的順序無關緊要。

+0

我嘗試在方法聲明上同步,但沒有嘗試使用塊...謝謝!我也一定要檢查信號量。 – Giannis

+0

是的,方法同步將不會在那裏工作,因爲它在Runnable類上,這意味着每個線程都有一個單獨的實例。他們需要在一個共同的對象上進行同步;在這種情況下。 –

0

-我認爲更好的方法在這裏是java.util.Semaphores ,這將幫助您一次確定通過特定數量的線程訪問對象資源。

-那麼你也可以使用SingleThreadExecutor來解決這個問題,其啓動並在移動到第二個任務之前完成任務,所以會有沒有從你身邊需要在這裏需要同步。

0

我不認爲你需要任何協調在這裏。只需要一個線程寫入偶數位置,另一個線程寫入奇數位置。讓他們都儘可能快地走。完成!

+0

這是兩個線程需要在陣列的每個位置上寫入的要求的一部分。 – Giannis