knpwrs
knpwrs

Reputation: 16516

Java Synchronization Problems

I am just starting to learn about Java concurrency and I am getting some strange output from the following code:

ThreadSafe.java:

public class ThreadSafe {
    private int value;

    public ThreadSafe() {
        value = 0;
    }

    public synchronized int getNext() {
        if (value >= 10000) {
            value = 0;
        }
        return value++;
    }
}

RunnableImpl.java:

public class RunnableImpl implements Runnable {
    private String name;
    private ThreadSafe ts;

    public RunnableImpl(String name, ThreadSafe ts) {
        this.name = name;
        this.ts = ts;
    }

    @Override
    public void run() {
        while (true) {
            System.out.println(name + ": " + ts.getNext());
        }
    }
}

Main.java:

public class Main {
    public static void main(String[] args) {
        ThreadSafe ts = new ThreadSafe();

        Runnable a = new RunnableImpl("A", ts);
        Runnable b = new RunnableImpl("B", ts);

        new Thread(a).start();
        new Thread(b).start();
    }
}

Whenever the threads switch off, I get output like the following:

B: 7320
B: 7321
A: 4278 // What?
A: 7323
A: 7324

Going back up the output where A and B switch off:

A: 4275
A: 4276
A: 4277
B: 2279 // ROBBLE ROBBLE!
B: 4279
B: 4280

I can see that A left off at 4277 earlier on and picked up at 4278 before skipping 7322 and continuing on with 7323. This happens with both value++ and ++value. My understanding was that synchronized would let each thread's method invocation of getNext() complete before letting any other threads execute that method.

When I use private volatile int value; in threadsafe.java I get output similar to the following:

A: 8511
A: 8512
A: 8513
B: 7022 // Meh.
B: 8514
B: 8515

This time no numbers were skipped (from ++value, still skips with value++) but the output still seems to be using a cached value for the first go.

What am I overlooking here?

Upvotes: 2

Views: 2050

Answers (1)

vanza
vanza

Reputation: 9903

Your ThreadSafe class is fine. This is happening most probably because of the thread scheduling. Namely, in the first example, thread "A" got de-scheduled after fetching 4278 from the instance, but before printing it to the terminal.

Then "B" ran for a while, fetching all values up to 7322, and printing all values up to 7321 before being de-scheduled, at which point "A" was scheduled again, and its next step was to print the value it had previously fetched (4278), and the continuing with the next value from the ThreadSafe class (7323).

So your problem is that while fetching the numbers is properly thread-safe, printing them is not.

Upvotes: 6

Related Questions