3

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

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: 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)
4

3 回答 3

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 有机会 wait() 之前发送了它的 notifyAll()。

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

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

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

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

于 2012-11-30T21:47:57.200 回答
0

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

-好吧,您也可以使用SingleThreadExecutor来解决这个问题,它会在继续执行第二个任务之前启动并完成一个任务,因此您这边不需要同步。

于 2012-11-25T15:19:48.707 回答
0

我认为你在这里不需要任何形式的协调。只需让一个线程写入偶数位置,另一个线程写入奇数位置。让他们俩尽可能快地走。完毕!

于 2012-11-26T07:15:54.627 回答