简体   繁体   中英

Java - Thread and Static variable

Just started with threads in java and I can't reason with the output of my program

public class ThreadExample extends Thread{
    private int info;
    static int x = 0;

    public ThreadExample (int info) {
        this.info = info;
    }

    public void run () {
        if ( info == 1 )    {
            x = 3;
            System.out.println(Thread.currentThread().getName() + " " + x);
        } else{
            x = 1;
            System.out.println(Thread.currentThread().getName() + " " + x);
        }
    }

    public static void main (String args []) {
        ThreadExample aT1  = new ThreadExample(1);
        ThreadExample aT2  = new ThreadExample(2);
        aT1.start();
        aT2.start();
        System.err.println(x);
    }
}

Output:

Thread-0 3
Thread-1 1
3

Why does it print 3 even though the 2nd thread changed the value of the static variable to 1?

Will there be 3 threads running concurrently?

If you change a variable in one thread it not immediately (or necessary ever ) visible to a 2nd thread unless you use some kind of synchronization primitive like a Mutex . You can also use the atomic classes like AtomicInteger to ensure changes made in one thread become visible to the other.

There's a lot more information available in the documentation .

Two possible scenarios

  1. Thread 2 would have updated x before Thread 1. You cannot determine how the execution interleaved between the two threads based on the order of the print statements you are seeing.

  2. The threads indeed executed in the order you expect. But since x is not volatile you might not see the updated value.

See - What is the volatile keyword useful for

You cannot predict the result of threading.

It may be different if you run your code on another device or just multiple times.

You cannot (or should not) rely on timing or the scheduler.


I think that concurrency/non-volatility itself may not be the only problem but flushing is also something you may want to take into consideration:

x=3 (ThreadExample(1))
sysout 3 (ThreadExample(1))
syserr x (main thread)
x=1 (ThreadExample(2))
sysout 3 (ThreadExample (2))
flush stdout (caused by jvm exit)
flush stderr (caused by jvm exit)

Note the flush at the end. stdout and stderr may not be synchronized.

Those streams are buffered and written to the console at any time.

While two things written to stdout or stderr are guaranteed to be written in the correct order, this is not assured if you pring one thing to stdout and another thing to stderr.

It is also guaranteed that everything printed to stdout and stderr is written when the jvm terminates normally(no kill -9 or similar).

If the jvm writes stdout before stderr , you can get your result.


If you want the output to be printed correctly, you may want to do two things:

  • Call flush manually after printing

  • create a synchronized block(or similar) around the operation, the println and the flush . (Note that you may lose a bit of performance/parallelism with that)

If you want to test if flushing makes a difference in your case, add System.err.flush(); (so that stderr is flushed before stdout )at the end of your profram and see if there is a difference.


Also, there obe more thing that I didn't find in other answers, explicitly: JIT optimization.

The JIT compiler may make optimizations to your program. For example, it could optimize:

x=3;
System.out.println(x);

to:

x=3;
System.out.println(3);

so that it prints 3 , even if it is not 3 at the time the println is called.

Variables are not recommended way to exchange information between threads. Use BlockingQueues for messages, Semaphores and CountDownLatches for signals. In short, transfer of a value must not only make silent assignment, but also create some kind of event, to notify other threads. I like word "token" for such objects.

Will there be 3 threads running concurrently?

Yes. The first thread is the main thread, the one which started it all, the one which invoked your public static void main (String args []) method. All code runs on a thread. Your main method then starts 2 threads. Since you started with 1, you now have 3.

As for why the final output from the main thread is 3 is hard to answer because you have a race condition. You have 3 threads reading a variable while 2 of them update, and these all happen concurrently.

x = 3;
System.out.println(Thread.currentThread().getName() + " " + x);

With 3 threads running, it's easy to assume the output of System.out.println above will be 3 , but the reality is, after setting it to 3 , another thread could have updated it and then when you print it, it's no longer 3.

Also consider the volatile keyword. Without it, the JVM may cache within a thread copies of shared values, which can lead to staleness when reading and writing across threads. What is the volatile keyword useful for

The outcome of threads is unpredictable.

To ensure consistent/predictable behavior use volatile/Atomic values to make the change visible to other threads

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM