user1330974
user1330974

Reputation: 2616

The output of the counter to this multi-threaded code

I came across a Java problem about multi-threaded programming (please see the code below). Based on this question and answer on StackOverflow, I think I understand why there could be a deadlock. But what I don't understand was if the program works correctly (i.e. there is no deadlock), what would be the value of foo printed? I thought it would be 20 (thread1 counting up to 10 and thread2 counting up to 10 more). Could someone help me explain how this might (preferably in a simple way because I'm still new to thread programming)? Thank you.

public class ThreadTest{

  private static class ThreadOne extends Thread{
    private ThreadTwo threadTwo; 
    public int foo = 0;

    public void setThreadTwo(ThreadTwo th){
       threadTwo = th;
    }

    public void run(){
      try{
        for(int i=0;i<10;i++) foo += i;

        synchronized(this){this.notify();};

        synchronized(threadTwo){threadTwo.wait();};

        System.out.print("Foo: " + threadTwo.foo);
      }catch(InterruptedException e){ e.printStackTrace();}    
    }
  }

  private static class ThreadTwo extends Thread{

    private final ThreadOne threadOne;
    public int foo = 0;

    public ThreadTwo(ThreadOne th){
      threadOne = th;
    }

    public void Run(){
       try{
         synchronized(threadOne){threadOne.wait();}
         foo = threadOne.foo;

         for(int i=0;i<10;i++) foo += i;
         synchronized(this){this.notify();};
           }
       catch(InterruptedException e){e.printStackTrace();}    
    }    
  }

  public static void main(){
    ThreadOne th1 = new ThreadOne();
    ThreadTwo th2 = new ThreadTwo(th1);

    th1.setThreadTwo(th2);

    th1.start(); th2.start();
    th1.join(); th2.join();
  } 
}

Upvotes: 1

Views: 86

Answers (1)

esin88
esin88

Reputation: 3199

According to your code and without deadlocks foo value will be 90 (if i didn't miscalculate). Because instead of foo += 1 you did foo += i.

EDIT: Okay, step by step.

  1. foo = 0
  2. th1 and th2 starts. th2 waits for notify. th1 increments foo up to 45
  3. th1 notifies and starts to wait th2. th2 is notified and starts to increment foo from 45 to 90
  4. th2 notifies th1. th1 is notified, and it prints th2.foo, which is 90

EDIT 2: Correct way to count from 0 to 90 from 2 threads without concurrent modification is something like this

public class ThreadTest {
    private static int counter = 0;

    private static class Thread1 extends Thread {
        final Object lock;

        public Thread1(Object lock) {
            this.lock = lock;
        }

        @Override
        public void run() {
            synchronized (lock) {
                for (int i = 0; i < 10; i++)
                    counter += i;
            }
        }
    }

    private static class Thread2 extends Thread {
        final Object lock;

        public Thread2(Object lock) {
            this.lock = lock;
        }

        @Override
        public void run() {
            synchronized (lock) {
                for (int i = 0; i < 10; i++)
                    counter += i;
            }
        }
    }

    public static void main(String[] args) {
        final Object lock = new Object();

        final Thread th1 = new Thread1(lock);
        final Thread th2 = new Thread2(lock);

        th1.start();
        th2.start();

        try {
            th1.join();
            th2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Counter: " + counter);
    }
}

But if you are forced to use wait and notify, than it's a bit more complicated. Use object of this class as common lock instead of Object

class Locker {

        private boolean isLocked = false;

        public synchronized void lock() throws InterruptedException {
            while (isLocked) wait();
            isLocked = true;
        }

        public synchronized void unlock() {
            isLocked = false;
            notify();
        }
    }

And in run method us it like this:

@Override
public void run() {
    try {
        locker.lock();
        for (int i = 0; i < 10; i++)
            counter += i;
        locker.unlock();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

Upvotes: 1

Related Questions