Question

I wrote a code snippet that starts two threads; one thread prints all odd numbers while another thread prints all even numbers. I used a combination of intrinsic lock and thread communication commands to achieve proper interleaving of my two threads. Here is my code,

public class threadEvenOdd implements Runnable
{
    static Boolean isOdd=true;
    int count = 10;
    Boolean value;
    static int c=1;
    static Object lock = new Object();

    threadEvenOdd(Boolean temp)
    {
        value = temp;
    }
    public void run()
    {
        if(value)
        {
            printOdd(count);
        }
        if(!value)
        {
            printEven(count);
        }
    }
    void printOdd(int count)
    {
        try
        {
            for(int i=0;i<count/2;i++)
            {
                //System.out.println("odd enters lock");
                synchronized(lock)
                {
                    if(!isOdd)
                    {
                        //System.out.println("odd in barrier");
                        lock.wait();
                    }
                    System.out.println(c);
                    c++;
                    isOdd = false;
                    //System.out.println("odd notifies");
                    lock.notify();
                }
            }
        }
        catch(Exception e)
        {
            System.out.println(e);
        }
    }
    void printEven(int count)
    {
        try
        {
            for(int i=0;i<count/2;i++)
            {
                //System.out.println("even enters lock");
                synchronized(lock)
                {
                    if(isOdd)
                    {
                        //System.out.println("even in barrier");
                        lock.wait();
                    }
                    System.out.println(c);
                    c++;
                    isOdd = true;
                    //System.out.println("even notifies");
                    lock.notify();
                }
            }
        }
        catch(Exception e)
        {
            System.out.println(e);
        }
    }
    public static void main (String args[])
    {
        threadEvenOdd th1 = new threadEvenOdd(true);
        threadEvenOdd th2 = new threadEvenOdd(false);
        Thread t1 = new Thread(th1);
        t1.setName("odd");
        Thread t2 = new Thread(th2);
        t2.setName("even");
        //System.out.println(t1.getName() + " starts");
        t1.start();
        //System.out.println(t2.getName() + " starts");
        t2.start();
    }
}

Here are my questions:

  1. The odd thread executes in the printOdd() function, while the even thread executes in the printEven() function. I am using one intrinsic lock for both threads; I don't understand how the two threads can exist in their respective synchronized blocks at the same time, because the use the same lock.

  2. I removed the thread communication statements(notify, wait) from my code and still I obtained my desired output. I am now wondering if my code actually needs the thread communication statements at all.

  3. I guess I still need to work on my understanding of multithreading concepts, as I am struggling to understand my own code :p Can anyone explain if there is a better way to do this using only the multithreading concepts that I have used?

Was it helpful?

Solution

  • Each thread has its own path of execution through the code. Even if two threads run the exact same code they still have two distinct execution points through the code execution through the code. When a thread reaches a synchronized statement it waits for the lock to be available - it will enter the synchronized block only if no other thread is inside a synchronized block guarded by the same lock.

  • You keep getting the same output although you removed the notify/wait statements can be coincidental. Did you try this with a relatively large value of the count field?

  • It is kind of hard to answer this question at the moment as you didn't specify what output do you expect this program to produce. Is "1,3,5,7,9,2,4,6,8" a valid output? Is "1,3,2,4,6,5,7,9,8"? Or is "1,2,3,4,5,6,7,8,9" the only valid output? That said, here a few quick points:

  • Use notifyAll() instead of notify

  • Minimize the state that is shared between threads. In this case, you share both isOdd and c. Note that the former can be computed from the latter via c % 2 == 1. Thus you can have the thread computing oddness instead of maintaining it as a piece of shared data.

  • Instead of sharing via static fields create an object (with instance fields) and pass this object to the constructor of each thread. Then you can use the object itself as a lock.

Here's how it can look like:

class SharedData {
    int c;
    boolean isOdd;
} 

class ThreadEvenOdd {
    SharedData sharedData;

    public ThreadEvenOdd(SharedData sd) { this.sharedData = sd }

    // ...

    void printOdd(int count) {
        try {
            for(int i=0;i<count/2;i++) {
                synchronized(sharedData) {
                    if(!sharedData.isOdd) { ... }
                    System.out.println(sharedData.c);
                    sharedData.c++;
                    sharedData.isOdd = false;
                    lock.notify();
                }
            }
        }
        catch(Exception e) {
            System.out.println(e);
        }
    }
}   

The nice thing about it is that you can then start defining real methods on sharedData (such as: a method that increases c and set isOdd to the appropriate value based on the value of c thus further simplifying the code in the thread class - and making the synchronization/notification less interleaved with the processing of the data, which makes the code more readable and less prone to errors.

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