This week's book giveaway is in the Mac OS forum.
We're giving away four copies of a choice of "Take Control of Upgrading to Yosemite" or "Take Control of Automating Your Mac" and have Joe Kissell on-line!
See this thread for details.
The moose likes Threads and Synchronization and the fly likes Output even more unexpected than I was expecting! Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login


JavaRanch » Java Forums » Java » Threads and Synchronization
Bookmark "Output even more unexpected than I was expecting!" Watch "Output even more unexpected than I was expecting!" New topic
Author

Output even more unexpected than I was expecting!

Simon Birch
Greenhorn

Joined: Dec 26, 2003
Posts: 21
I've been experimenting with threads, and wrote the following two classes to test my understanding:




So, I was expecting output similar to:
Thread t1 i=0 p=0
Thread t1 i=1 p=1
Thread t3 i=0 p=2
Thread t2 i=0 p=3
Thread t1 i=2 p=4
Thread t3 i=1 p=5
Thread t2 i=1 p=6

My reasoning was:
  • I would expect the threads to execute in unpredictable order, so the output ought to be a random mix from the threads (i.e. anything from one after another to total mish-mash)
  • As i is a local variable, I would expect to see it increase by 1 each time a line was output from a thread. So all thread 1 output would have i incrementing in sequence, all thread 2 output would have it increments in sequence, etc.
  • As p is a class variable, and therefore shared between the threads, and incremented by all of them, I would expect to see p increase from 1 on the first line of output, to 2 on the second, 3 on the third, etc, regardless of the order in which the threads are executed.



  • Obviously I was wrong or I wouldn't be posting this. A sample of the actual output follows:
    Thread t2 i=0 p=0
    Thread t2 i=1 p=3
    Thread t2 i=2 p=4
    Thread t2 i=3 p=5
    Thread t2 i=4 p=6
    Thread t2 i=5 p=7
    Thread t2 i=6 p=8
    Thread t2 i=7 p=9
    Thread t2 i=8 p=10
    Thread t2 i=9 p=11
    Thread t2 i=10 p=12
    Thread t3 i=0 p=1
    Thread t3 i=1 p=13
    Thread t3 i=2 p=14
    Thread t3 i=3 p=15
    Thread t3 i=4 p=16
    Thread t3 i=5 p=17
    Thread t3 i=6 p=18
    Thread t3 i=7 p=19
    Thread t3 i=8 p=20
    Thread t3 i=9 p=21
    Thread t3 i=10 p=22
    Thread t1 i=0 p=2
    Thread t1 i=1 p=23
    Thread t1 i=2 p=24
    Thread t1 i=3 p=25
    Thread t1 i=4 p=26
    Thread t1 i=5 p=27
    Thread t1 i=6 p=28
    Thread t1 i=7 p=29
    Thread t1 i=8 p=30
    Thread t1 i=9 p=31
    Thread t1 i=10 p=32

    I'm confused. Can anyone help clear up what is going on?
    Henry Wong
    author
    Sheriff

    Joined: Sep 28, 2004
    Posts: 18876
        
      40

    output ought to be a random mix from the threads (i.e. anything from one after another to total mish-mash)
    I would expect the threads to execute in unpredictable order, so the As i is a local variable, I would expect to see it increase by 1 each time a line was output from a thread. So all thread 1 output would have i incrementing in sequence, all thread 2 output would have it increments in sequence, etc.


    Your program is pretty short, meaning the loop is pretty small, so don't be surprise if it looks likes the threads are doing multiple interations per timeslice. Remember that printing is done to buffers then eventually flushed so that won't slow it down. If you want a better mix, simulate actually doing stuff, like a complex calc, prior to printing.

    As p is a class variable, and therefore shared between the threads, and incremented by all of them, I would expect to see p increase from 1 on the first line of output, to 2 on the second, 3 on the third, etc, regardless of the order in which the threads are executed.


    First of all, p is not a class variable. This program works because all the threads are using the same object... but to answer your question... it is being incremented by all and hence are processed in order, but the printing is not in order. It is a very subtle distinction, but just think about it a bit.

    And finally, this class is not thread safe. The threads are all using the P variable without synchronization. You are actually lucky that you are not get weird values for p -- like two threads with the same p and such.

    Hope this helps,
    Henry


    Books: Java Threads, 3rd Edition, Jini in a Nutshell, and Java Gems (contributor)
    Mr. C Lamont Gilbert
    Ranch Hand

    Joined: Oct 05, 2001
    Posts: 1170

    Ahh Henry, for shame

    You said p was not a class variable, then you went on to call the program unsafe due to p.

    'p' is what we call a member variable. Its a member of the class. I am not entirely sure i remeber the proper usage of the term 'class variable' so I will leave that alone.

    The point is each instance of the class gets its own copy of member variables. If you wanted p shared between instances you should have made it static.



    now you will have succeeded in creating a thread unsafe program, happy?

    And for the questions
    1. computers are not truly random. The OS knows the order. The OS is going to choose which threads to run when, and it would be less efficient to run 1 thread for a microsecond, then switch to another thread. So its gonna stick with 1 thread for a while. If you had an actual dual processor computer then you would see more agressive switching by the OS. Try slapping some Thread.yield calls in there and see if you can have any more fun.

    2. sounds right.

    3. see above.


    Since your threads are all daemon threads your program waits on them all. But you should probably have a thread.join in your main method, and make your other threds non-daemon threads. Its a design choice in truth.
    Ilja Preuss
    author
    Sheriff

    Joined: Jul 11, 2001
    Posts: 14112
    Originally posted by CL Gilbert:
    The point is each instance of the class gets its own copy of member variables. If you wanted p shared between instances you should have made it static.


    All the threads are using the same instance. Therefore it's not threadsafe the way it's written.


    The soul is dyed the color of its thoughts. Think only on those things that are in line with your principles and can bear the light of day. The content of your character is your choice. Day by day, what you do is who you become. Your integrity is your destiny - it is the light that guides your way. - Heraclitus
    Henry Wong
    author
    Sheriff

    Joined: Sep 28, 2004
    Posts: 18876
        
      40

    Ahh Henry, for shame

    You said p was not a class variable, then you went on to call the program unsafe due to p.


    CL, cool similey, I couldn't tell during posting what that one actually did... although the part of me from Brooklyn thought it did something else...

    And as Ilja explained, I stand by what I said.

    Henry
    [ October 16, 2004: Message edited by: Henry Wong ]
    Mr. C Lamont Gilbert
    Ranch Hand

    Joined: Oct 05, 2001
    Posts: 1170

    yes. I have to admit to never noticing that possibility. Been writing Java for years and that fact never occurred to me.

    I really don't know what I been thinking all these years. I gotta go double check some old code
    Simon Birch
    Greenhorn

    Joined: Dec 26, 2003
    Posts: 21
    Thanks for all the comments. The program wasn't intended to be used in any way. It was simply a test to see if I understood thread behaviour.

    I was making an unfounded assumption though - I assumed that a thread could only be interrupted when a line of code had finished executing.

    Looking at the output, I'm thinking that the program executed thread 2 up to the point where it has incremented p, but before it has constructed the String to be output. Processing then switched to thread 3, which was swapped out at the same point - after p has incremented, but before the output String is constructed. We then swap to thread 1, stop processing at the same point and then switch back to thread 2, which picks up by constructing the output String and outputing it. Thread 2 runs until it hits the end of the loop. Processing then switches to thread 3, and so on - I'll stop there, you're probably bored.

    Because I was expecting a whole line of code to be executed, I was expecting different output.

    I know the code as it stands is not threadsafe - but that was deliberate so I could get a handle on how threads worked.

    Once again, many thanks for all your comments. Please correct me though if the above is incorrect.
    David Harkness
    Ranch Hand

    Joined: Aug 07, 2003
    Posts: 1646
    Originally posted by Simon Birch:
    I was making an unfounded assumption though - I assumed that a thread could only be interrupted when a line of code had finished executing.

    Not only can a thread be interrupted while executing a line consisting of expressions, but so can writing a value to a long or double variable! This is because those are 64-bit values while the JVM is 32-bit. Threading can get quite tricky.
    Warren Dew
    blacksmith
    Ranch Hand

    Joined: Mar 04, 2004
    Posts: 1332
        
        2
    Simon Birch:

    Looking at the output, I'm thinking that the program executed thread 2 up to the point where it has incremented p, but before it has constructed the String to be output. Processing then switched to thread 3, which was swapped out at the same point - after p has incremented, but before the output String is constructed. We then swap to thread 1, stop processing at the same point and then switch back to thread 2, which picks up by constructing the output String and outputing it. Thread 2 runs until it hits the end of the loop. Processing then switches to thread 3, and so on - I'll stop there, you're probably bored.

    Yes, that's basically what Henry said; the only difference is that he suspect the interruption happened during the call to System.out.println() rather than during the String constructor.

    As Henry noted, though, things can get much worse than that. While the Java language specification requires execution within a single thread to proceed "as if ordered", this is not true across threads: from the point of view of thread t2, it might look like things in thread t1 are proceeding out of order.

    You could very easily get output like:

    Thread t2 i=0 p=10
    Thread t2 i=1 p=11
    Thread t3 i=0 p=10
    Thread t3 i=1 p=11
    Thread t1 i=0 p=0
    Thread t1 i=1 p=1
    etc.

    Because it might look to t2 like t1 had completed before t2 got to execute, and it might look to t3 like t2 hadn't executed at all by the time t3 started printing. In fact, depending on how System.out.println() is actually implemented, the above could result from a simple optimization that stored the value of p in a register during the loop.

    There are even wierder things that can theoretically happen prior to Java 1.5 - roughly equivalent to p starting to print with, say, a value of 42 in your example.

    The bottom line is, always use proper synchronization if you are going to communicate values across threads.
    Mr. C Lamont Gilbert
    Ranch Hand

    Joined: Oct 05, 2001
    Posts: 1170

    What is special about java 1.5? Is there a new specifications?
    Henry Wong
    author
    Sheriff

    Joined: Sep 28, 2004
    Posts: 18876
        
      40

    Well... 1.5 added a ton of stuff to make thread coding easier. This includes synchronization classes, services (like pools and schedulers), and the atomic library (which for those people who like to program threads optimistically, finally enables the practice)

    But... while there were some JVM improvements, I don't think anything was added to get rid of race conditions automatically -- at least, that I know of.

    Henry
    scott p laplante
    Greenhorn

    Joined: Mar 12, 2004
    Posts: 13
    as for te claims that this is not thread safe, I have a question...

    are both ++p and p++ atomic operations? if so, i don't see how this is not thread safe.
    Madhav Sapre
    Greenhorn

    Joined: Jan 03, 2004
    Posts: 1
    Hi Simon,
    As pointed out earlier by someone else, p here is not defined as class variable. Secondly, if you define i as volatile, that will force each thread to refresh its local copy of i before it actually tries to modify i. So Try following code.

    public class Unstable implements Runnable {
    private int p = 0;
    private volatile int i = 0;

    public void run() {
    for(; i <= 10; i++) {
    System.out.println("Thread " + Thread.currentThread().getName() + " i=" + i + " p=" + p++);
    }
    }
    }
    Alexandru Popescu
    Ranch Hand

    Joined: Jul 12, 2004
    Posts: 995
    Originally posted by scott p laplante:
    as for te claims that this is not thread safe, I have a question...

    are both ++p and p++ atomic operations? if so, i don't see how this is not thread safe.


    All three threads access the same instance of the object so making any assumption on the field value would be hazardous.
    I don't know what are you refering as atomic? (one JVM instruction?)

    ./pope


    blog - InfoQ.com
    Alexandru Popescu
    Ranch Hand

    Joined: Jul 12, 2004
    Posts: 995
    Simon you must run the application for a longer time in order to allow the machine to schedule (interpose) the threads.

    ./pope
     
    It is sorta covered in the JavaRanch Style Guide.
     
    subject: Output even more unexpected than I was expecting!