2012-11-25 29 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