views:

65

answers:

2

It might be a silly question but, can the output of this program ( the way it is) be zero?

public class Test2{

    int a = 0;
    AtomicInteger b = new AtomicInteger();
    public static Test2  c = new Test2();

    public static void main(String[] args){

        Thread t1 = new Thread(new MyTest1());
        Thread t2 = new Thread (new Mytest2());

        t1.start();
        t2.start();
    }

}

class MyTest1 implements Runnable{

    public void run(){
        Test2.c.a = 1;
        Test2.c.b.compareAndSet(0,1);

    }
}

class Mytest2 implements Runnable{
    public void run(){
        int x = 0;

        if(Test2.c.b.get() == 1)
            x = Test2.c.a;

        System.out.println("Value of x = "+x);
    }

}

The reason I'm asking this is, although I'm using AtomicInteger, the if() statement in MyTest2 might be executed first and then x will have the value zero..right?

or am I not thinking straight.

Any help would be highly appreciated.

+5  A: 

can the output of this program ( the way it is) be zero?

Yes, that's possible. The code does not guarantee in any way that t1 will finish before t2 finishes. If that's your intent, you may find CountdownLatch or CyclicBarrier useful. Click the links, their javadocs contains code examples.


That said, I'd rather pass a reference of the AtomicInteger as constructor argument of the both runnables instead of accessing it the static way.

BalusC
Thanks... I understand... I just saw the piece of code somewhere and the comments read "output will be 1" -- and hence the reason I posted it here.
Achilles
The thing that's bothering me is the fact that if you keep the above statements in this order:t1.start();t2.start();Then the output is always 1, but if you reverse the ordering to:t2.start();t1.start();Then the output is always 0.Which is really strange cuz even after a 100 runs, i can't make it produce different output for the same ordering.
Achilles
Maybe you've a single core CPU. At my dual core machine, I see `0` about once every 5 runs. At any way, the code posted as far does not guarantee synchronization of the threads in any way. The threads lead both their own life without some synchronization aid. For another code example using `CyclicBarrier` you may find [this answer](http://stackoverflow.com/questions/3379797/java-threaded-serial-port-read-with-java-util-concurrent-thread-access/3380175#3380175) useful.
BalusC
Many thanks mate.
Achilles
You're welcome.
BalusC
Achilles - that's one of the thorniest issues with concurrency errors in general. It's often the case that you can write code that is ambiguous according to the JLS, but yet which produces the expected result on your specific combination of JVM and hardware. Your JVM *could* produce an alternative output, but in practice never does. When the code runs elsewhere, all of a sudden it's failing due to the other environment tripping on the ambiguity.
Andrzej Doyle
+1  A: 

With one small change you can guarantee that the output will be 1:

public void run(){
    int x = 1;

    if(Test2.c.b.get() == 1)
        x = Test2.c.a;

    System.out.println("Value of x = "+x);
}

Because of the ordering guarantees of volatile reads/writes:

  • x is initally 1
  • The only other value that can be assigned to x is the value of Test2.c.a
  • Test2.c.a can be copied to x only if 1 was previously read from Test2.c.b
  • 1 is written to Test2.c.b only after writing 1 to Test2.c.a
  • Because Test2.c.b is volatile, any thread that reads it also observes all previous writes to all variables (volatile or not) by the same thread that wrote to Test2.c.b. That means t2 also sees the write of 1 to Test2.c.a
  • If the test fails (i.e. the read of Test2.c.b returns something other than 1) then x retains its initial value of 1, so the output is still 1.

I suspect this is what the author of this program was trying to illustrate, and the default value of 0 for x was a typo.

finnw
Nice explanation, but IMO the original author simply made a thinking mistake and changing the default return value is not a solution but a workaround. What's the point of using threads for the purpose then?
BalusC