1

This is my code and output are different anytime that I run the code. Sometimes all three readers will be notified and output is:

Waiting for calculation...

Waiting for calculation...

Waiting for calculation...

Finished

Total is: 4950Thread-1

Total is: 4950Thread-2

Total is: 4950Thread-0

and sometimes just two or one reader will be notified. what is the problem?

class Reader extends Thread {
    Calculator c;

    public Reader(Calculator calc) {
        c = calc;
    }

    public void run() {
        synchronized (c) {
            try {
                System.out.println("Waiting for calculation...");
                c.wait();
            } catch (InterruptedException e) {
            }
            System.out.println("Total is: " + c.total +Thread.currentThread().getName());
        }
    }

    public static void main(String[] args) {
        Calculator calculator = new Calculator();
        new Reader(calculator).start();
        new Reader(calculator).start();
        new Reader(calculator).start();
        new Thread(calculator).start();
    }
}

class Calculator implements Runnable {
    int total;

    public void run() {
        synchronized (this) {
            for (int i = 0; i < 100; i++) {
                total += i;
            }
            System.out.println("Finished");
            notifyAll();
        }
    }
}

As per the meta post, this question is claimed to be a duplicate, but both "duplicates" that have been dupe-hammered simply do not apply. How to use wait and notify in Java? reminds users that if you truly want to wait on the same object, you have to synchronize on that object. But this solution is already doing this. Java: notify() vs. notifyAll() all over again reminds users the difference between notify and notifyAll which is even further from the problem.

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
navid
  • 35
  • 6
  • Works for me on MacOS/JDK 1.8! – fhossfel Jul 20 '17 at 23:09
  • 1
    It's possible for Calculator to notify before Reader starts to wait. – Grief Jul 20 '17 at 23:12
  • 1
    BTW extending Thread is not that advisable. – bichito Jul 20 '17 at 23:17
  • [How much research effort is expected of Stack Overflow users?](https://meta.stackoverflow.com/questions/261592/how-much-research-effort-is-expected-of-stack-overflow-users) –  Jul 20 '17 at 23:22
  • 1
    @JarrodRoberson This is not a duplicate question. The answer to that question is *already in use in his question*. Rather, he is doing exactly that but is failing to account for subtle complexities of the thread scheduler. – corsiKa Jul 20 '17 at 23:24
  • 2
    Opened a meta question to address this - https://meta.stackoverflow.com/questions/352598/could-we-please-stop-dupe-hammering-this-question – corsiKa Jul 21 '17 at 02:04

3 Answers3

2

I was able to reproduce the problem - basically, if you have too fast of a computer, or a bad scheduling, it's possible for the calculator to finish before the reader has an opportunity to synchronize and wait.

c:\files\j>java Reader
Waiting for calculation...
Waiting for calculation...
Finished
Waiting for calculation...
Total is: 4950Thread-2
Total is: 4950Thread-0

To prevent this, you should verify that all your readers are ready before performing the calculation.

c:\files\j>java Reader Waiting for calculation... Waiting for readers... currently 1 Waiting for calculation... Waiting for calculation... Finished Total is: 4950Thread-1 Total is: 4950Thread-2 Total is: 4950Thread-0

Here is my code

import java.util.concurrent.atomic.AtomicInteger; // corsiKa added import
class Reader extends Thread {

    static class Calculator implements Runnable {
        int total;
        AtomicInteger readers = new AtomicInteger(0); // corsiKa added atomicinteger

        public void run() {
            // corsiKa added while
            while(readers.get() < 3) {
                System.out.println("Waiting for readers... currently " + readers.get());
                try { Thread.sleep(100); } catch(InterruptedException e) { }
            }
            synchronized (this) {
                for (int i = 0; i < 100; i++) {
                    total += i;
                }
                System.out.println("Finished");
                notifyAll();
            }
        }
    }

    Calculator c;

    public Reader(Calculator calc) {
        c = calc;
    }

    public void run() {
        synchronized (c) {
            try {
                c.readers.incrementAndGet(); // corsiKa added increment
                System.out.println("Waiting for calculation...");
                c.wait();
            } catch (InterruptedException e) {
            }
            System.out.println("Total is: " + c.total +Thread.currentThread().getName());
        }
    }

    public static void main(String[] args) {
        Calculator calculator = new Calculator();
        new Reader(calculator).start();
        new Reader(calculator).start();
        new Reader(calculator).start();
        new Thread(calculator).start();
    }
}
corsiKa
  • 81,495
  • 25
  • 153
  • 204
  • Thanks, I got the problem. I should add sleep method before synchronized block in run method – navid Jul 20 '17 at 23:18
  • Exactly! I was about to upload an example. – corsiKa Jul 20 '17 at 23:20
  • 4
    It's a pretty bad idea to use sleep to fix this issue. This reduces the likeliness of the problem to occur but does not address the synchronization issue apparent in the program. – Björn Zurmaar Jul 20 '17 at 23:23
  • @BjörnZurmaar It absolutely is. The entire program is also very bad, extending Thread, etc. It's a proof of concept to illustrate the problem, and a further proof of concept to illustrate the solution. – corsiKa Jul 20 '17 at 23:25
  • 1
    Ok, when combining the sleep approach with an atomic integer this will fix the issue. My previous comment was referring to the comments, not your answer. However, you're better of using the right tools from the java.util.concurrent package imho. – Björn Zurmaar Jul 20 '17 at 23:32
  • Ah, I see. Yes, personally I'd rather avoid any use of wait/notify in favor of the java.util.concurrent locks and latches. They're more logical and far more powerful. I think `synchronized` blocks are generally fine, but I find so often RWLocks are just better anyway for real world scenarios. – corsiKa Jul 20 '17 at 23:34
  • 1
    Indeed, something like a `CountDownLatch` would allow an IMHO far cleaner solution than the current atomic+sleep thingy that you created there. Even *if* the code in the question is poor, don't you think it's worth to 1. try to provide the "cleanest" possible solution in the "dirty" environment and 2. point out how to make the existing code "cleaner"? You know... people google, come here, and copy+paste this stuff. (And you can hardly blame them for that) – Marco13 Jul 22 '17 at 00:44
  • I absolutely can blame them for blindly copy and pasting into production code. And no, I don't believe the examples should be "clean" - I believe they should be conceptual. Many great libraries use terrible practices in their examples. I just came across just this morning, www.jmdns.org - obviously bad code, but gets the point across. – corsiKa Jul 24 '17 at 00:01
1

The problem with your code is that the thread's start order is not defined. The intuitively expected behavior is that the readers start first, then the calculator. This is exactly what happened in the log you show above.

But there are other interleavings possible as well. Calling Thread.start does not make any guarantees about the order of the starting. Assume two readers start first, then the calculator, then the last reader. In this case the calculator can enter the critical section before the third reader. The call of notifyAll from the calculator then happens before the third reader executes its wait call. Hence the third reader is damned to wait forever as not other call of notify or notifyAll will occur on the lock object.

One possible solution to your problem is to use a CountDownLatch which allows you to let the calculator wait until all three readers are ready.

Björn Zurmaar
  • 826
  • 1
  • 9
  • 22
0

Make your main method check the other Reader's states.

public static void main(String[] args) {
    Calculator calculator = new Calculator();
    Reader read1 = new Reader(calculator);
    read1.start();
    Reader read2 = new Reader(calculator);
    read2.start();
    Reader read3 = new Reader(calculator);
    read3.start();

    while(read1.getState() != Thread.State.WAITING && 
          read2.getState() != Thread.State.WAITING &&
          read3.getState() != Thread.State.WAITING){
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    new Thread(calculator).start();
}

This will insure that you absolutely have all the threads waiting, and yields the same result every time.

Funny Geeks
  • 483
  • 5
  • 12