线程调度 - 共享阵列

时间:2012-11-25 15:06:58

标签: java multithreading jpf

我需要两个线程来编写一个共享的int数组。两个线程都需要写入该数组的所有元素。每个线程将写入1或7,结果应该像171717171(或71717171)。为此,我在第0位写入第一个Thread1,然后等待。 Thread2现在写入位置0和1,通知Thread1,并等待。 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;
    }
}

p.s:执行的一个例子:

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: WriterThreadManager@152
  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: WriterThreadManager@152
  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)

3 个答案:

答案 0 :(得分: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

在这种情况下,你已经死锁,因为thread1在thread7有机会等待它之前发送了notifyAll()。

在不同的环境中运行可能会弄乱您的时间并导致出现这些类型的行为。

为避免这种情况,我建议这样做:

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

或者更好的是,使用信号量作为@KumarVivekMitra建议。信号量结合了通知系统和计数器,因此通知和等待的顺序无关紧要。

答案 1 :(得分:0)

- 我认为这里更好的方法是java.util.Semaphores,它可以帮助您决定一次按特定数量的线程访问对象资源。

- 那么你也可以使用SingleThreadExecutor来解决这个问题,在开始第二个任务之前启动并完成任务,所以会有不需要这里需要同步。

答案 2 :(得分:0)

我认为你不需要任何协调。只需要一个线程写入偶数位置,另一个线程写入奇数位置。让他们尽可能快地去。完成!