Question

I need two threads to write one a shared array of ints. Both threads need to write on all the elements of that array. Each thread will write either 1 or 7, and the result should be like 171717171 (or 71717171). To do that I have the first Thread1 write at position 0, then wait. Thread2 now writes at position 0 and 1, notifies Thread1, and waits. Thread1 writes at position 1 and 2, notifies Thread2 and waits, etc. With the following code I get correct output, although when run with JPF it finds a deadlock. Its become really frustrating since I can not find whats wrong with it. Any advice would be appreciated.

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: an example of the execution:

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

The error snapshot from JPF is the following:

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)
Was it helpful?

Solution

I believe the race is due to this method:

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

While the individual wakeUpThread() and stopThread() methods are synchronized, you have the opportunity for unexpected thread scheduling between these calls.

Consider:

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

In this case you have deadlocked because thread1 sent its notifyAll() before thread7 had a chance to wait() for it.

Running in a different context can mess with your timing and cause these types of behaviors to appear.

To avoid this I suggestion doing this:

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

Or better yet, use a semaphore as @KumarVivekMitra suggested. Semaphores combine both the notification system and a counter so that the order of the notify and wait don't matter.

OTHER TIPS

- I think a better approach here would be java.util.Semaphores, which will help you to decide the access over the objects resources by specific numbers of threads at a time.

- Well you can also use the SingleThreadExecutor to solve this, which starts and completes a task before moving on to the 2nd task, so there will be No need of synchronization needed here from your side.

I don't think you need any sort of coordination here. Just have one thread write the even locations and the other thread write the odd locations. Let them both go as fast as they can. Done!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top