Krzysztof Majewski
Krzysztof Majewski

Reputation: 2534

common data in two threads

I want to have two threads. One will increment my variable and the other one will show it on the screen just after it is incremented.

I want to use the wait() and notifyAll() functions to do that but I have some problems.

I have written this code, but it stops working at some point (it shows only the first and the last number: 1 and 10).

public class TestClass {
    static int x = 0;
    public static Object lock = new Object();

    public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable() {
            public void run() {
                try {
                    for(int i = 0; i < 10; i++) {
                        synchronized (lock) {
                            lock.wait();
                            System.out.println(x);
                        }
                    }
                } 
                catch (InterruptedException e) {
                    e.printStackTrace();
                }           
            }
        });
        t1.start();

        Thread t2 = new Thread(new Runnable() {
            public void  run() {
                for(int i = 0; i < 10; i++) {
                    synchronized (lock) {
                        x++;
                        lock.notifyAll();
                    }
                }
            }
        });
        t2.start();
    }
}

I have tried adding additional notifyAll() in the first thread and wait() in the second one(t2) but it still doesn't work. How can I do that right?

Upvotes: 0

Views: 216

Answers (3)

Miljen Mikic
Miljen Mikic

Reputation: 15241

Below is a working solution. Key is in adding an additional flag that will indicate whether the writing thread has the right to write. Note final on the lock object, that is a good practice.

public class TestClass
{
    private static int x = 0;
    private static final Object lock = new Object();  
    private static boolean canWrite = false;

    public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable() {
            public void run() {
                try {
                    for(int i = 0; i < 10; i++)
                    {  
                        synchronized(lock)
                        {                            
                            if(!canWrite)                            
                                lock.wait();
                            System.out.println(x);
                            canWrite = false;
                            lock.notify();
                        }
                    }                    
                } 
                catch (InterruptedException e) {}
            }
        });        

        Thread t2 = new Thread(new Runnable() {
            public void run() {
                try {
                    for(int i = 0; i < 10; i++)
                    {                                                
                        synchronized(lock)
                        {                                                              
                            x++;
                            canWrite = true;
                            lock.notify();
                            lock.wait();
                        }
                    }                            
                } catch (InterruptedException ex) {}                                                                        
            }
        });
        t1.start();        
        t2.start();
    }
}

Upvotes: 1

Derek Hulley
Derek Hulley

Reputation: 334

The pattern is confusing but if you want to try to control threads, I suppose it's a useful exercise.

Change your code to give your thread a good name e.g. "test1" and "test2", say. Run your program and dig out the process ID for it and run

jstack <pid>

(you can find it in the bin directory of the JDK). This is (perhaps) what you will see: test1 will be waiting one the

lock.wait();

while test2 will waiting on

lock.wait();

as well.

Start at the first iteration. test2 wins and calls notify, which has no effect at all because nothing is waiting on the lock. test2 then starts waiting. Now test1 can enter and immediately goes into a wait.

If test1 wins the first time, you can get into a cycle that appears to work.

That is why you get the first number (test2 wins first) or the last number (test1 wins).

That's pretty much what it looks like to me, but your jstack will tell you for sure. PS: Use CountDownLatch and the concurrency package in general.

Upvotes: 1

vandale
vandale

Reputation: 3650

From JavaDoc:

The awakened threads will not be able to proceed until the current thread relinquishes the lock on this object. The awakened threads will compete in the usual manner with any other threads that might be actively competing to synchronize on this object; for example, the awakened threads enjoy no reliable privilege or disadvantage in being the next thread to lock this object.

The thread that you just woke up is not guaranteed to acquire the lock immediately, thus you could have the loop that calls notifyAll() run all its iterations before the other gets woken up.

If you want them to alternate, one way is to have each one take turns waiting

Upvotes: 2

Related Questions