views:

91

answers:

6

This code should produce even and uneven output because there is no synchronized on any methods. Yet the output on my JVM is always even. I am really confused as this example comes straight out of Doug Lea.

public class TestMethod implements Runnable {

private int index = 0;

    public void testThisMethod() {
        index++;
        index++;
        System.out.println(Thread.currentThread().toString() + " "
                    + index );

    }

    public void run() {
        while(true) {
            this.testThisMethod();
        }
    }

    public static void main(String args[]) {
        int i = 0;
        TestMethod method = new TestMethod();
        while(i < 20) {
            new Thread(method).start();
            i++;
        }
    }
}

Output

Thread[Thread-8,5,main] 135134

Thread[Thread-8,5,main] 135136

Thread[Thread-8,5,main] 135138

Thread[Thread-8,5,main] 135140

Thread[Thread-8,5,main] 135142

Thread[Thread-8,5,main] 135144

A: 

You have not marked index as volatile. This means that the compiler is allowed to optimize accesses to it, and it probably merges your 2 increments to one addition.

Tassos Bassoukos
Triedvolatile private int index = 0;Did not change anything.
Excalibur2000
+3  A: 

You don't know that. The point of automatic scheduling is that it makes no guarantees. It might treat two threads that run the same code completely different. Or completely the same. Or completely the same for an hour and then suddenly different...

The point is, even if you fix the problems mentioned in the other answers, you still cannot rely on things coming out a particular way; you must always be prepared for any possible interleaving that the Java memory and threading model allows, and that includes the possibility that the println always happens after an even number of increments, even if that seems unlikely to you on the face of it.

Kilian Foth
Synchronization is overrated.. ;)No, seriously.. I totally agree with your post!
Javaguru
But the threads should interfere here and give me uneven counts some of the time. At least thats how I understand it. The scheduler does not control the lock on the object ?
Excalibur2000
They *might* interfere, but that's not in the contract between you and the JVM. If you need to ensure that it *doesn't* happen, then yes, you need synchronization. But that doesn't mean that java guarantees to interleave these statements if you don't synchronize, it only guarantees that it *doesn't* interleave if you *do* synchronize. You cannot (easily) provoke the actual interleaving behaviour you want to demonstrate; you need to know a lot about the internal state of the JVM, the environment, etc., and even then the behaviour can still change at a moment's notice.
Kilian Foth
+3  A: 

The result is exactly as I would expect. index is being incremented twice between outputs, and there is no interaction between threads.

To turn the question around - why would you expect odd outputs?

EDIT: Whoops. I wrongly assumed a new runnable was being created per Thread, and therefore there was a distinct index per thread, rather than shared. Disturbing how such a flawed answer got 3 upvotes though...

Visage
I need a synchronize on the method to secure the semaphore on the executing object. Otherwise multiple threads execute concurrently on the method.
Excalibur2000
The output from another thread can come before the second 'index' increment. There is a single runnable and multiple threads operating on that runnable. Bit confusing.
Chris Dennett
The interaction is a shared `index` variable.
Bakkal
+2  A: 

First off all: as others have noted there's no guarantee at all, that your threads do get interrupted between the two increment operations.

Note that printing to System.out pretty likely forces some kind of synchronization on your threads, so your threads are pretty likely to have just started a time slice when they return from that, so they will probably complete the two incrementation operations and then wait for the shared resource for System.out.

Try replacing the System.out.println() with something like this:

int snapshot = index;
if (snapshot % 2 != 0) {
  System.out.println("Oh noes! " + snapshot);
}
Joachim Sauer
@downvoter: care to comment to tell me why?
Joachim Sauer
After a few experimentations, I think this is the answer. Would you happen to know why the print never gets called right after the first `index++` incrementation in one of the threads?
Bakkal
+4  A: 

I tried with volatile and got the following (with an if to print only if odd):

Thread[Thread-12,5,main] 122229779
Thread[Thread-12,5,main] 122229781
Thread[Thread-12,5,main] 122229783
Thread[Thread-12,5,main] 122229785
Thread[Thread-12,5,main] 122229787

Answer to comments:

the index is infact shared, because we have one TestMethod instance but many Threads that call testThisMethod() on the one TestMethod that we have.


Code (no changes besides the mentioned above):

public class TestMethod implements Runnable {

    volatile private int index = 0;

        public void testThisMethod() {
            index++;
            index++;
            if(index % 2 != 0){
            System.out.println(Thread.currentThread().toString() + " "
                        + index );
            }

        }

        public void run() {
            while(true) {
                this.testThisMethod();
            }
        }

        public static void main(String args[]) {
            int i = 0;
            TestMethod method = new TestMethod();
            while(i < 20) {
                new Thread(method).start();
                i++;
            }
        }
    }
Bakkal
if(index % 2 > 0) System.out.println(Thread.currentThread().toString() + " " + index );This code prints the odd numbered output but without it I get just even output how strange is that ?
Excalibur2000
I also noted that only one thread is printing output.
Excalibur2000
@Excalibur: that's probably because the `System.out.println()` requires a shared resource to be aquired and as such influences the thread scheduling.
Joachim Sauer
A: 

You get the output of the very first thread you start, because this thread loops and gives no chance to other threads to run.

So you should Thread.sleep() or (not recommended) Thread.yield() in the loop.

PeterMmm
This seems to also be true. Sleeping in the Thread seems elicit the correct behaviour - the output is odd and even. Seems like you have to give other threads a chance to execute.
Excalibur2000
That's wrong. All threads will get a chance to run. Endless loops in one thread will not force other threads to wait forever.
Joachim Sauer
This actually worked with the least code modification. I also puzzled. I would have thought that other threads are not starved but this seems to be the case.
Excalibur2000
The proof that all threads run comes from the fact that putting the if statement in catches out all the odd output. I have to go with Joachim on this one. The output is just flawed by a synchronous access to System.out.
Excalibur2000
That's wrong. Evidence in the output logs: the thread numbers, `currentThread()` returns a reference to the currently executing thread object, which is not all the time the same one.
Bakkal
My apologies Bakal.
Excalibur2000
@Excalibur2000 that was to the answer of @PeterMmm :P
Bakkal