Java – 线程和静态变量

huangapple go评论95阅读模式
英文:

Java - Thread and Static variable

问题

Here is the translated code and question:

刚刚开始学习Java中的线程,但无法理解程序的输出。

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);
    }
}

输出结果:

Thread-0 3
Thread-1 1
3

为什么输出是3,尽管第二个线程将静态变量的值更改为1?是否会有三个线程同时运行?

英文:

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?

答案1

得分: 5

如果您在一个线程中更改变量,除非使用某种同步原语(如 Mutex),否则不会立即(或可能根本不会)对第二个线程可见。您还可以使用原子类,如 AtomicInteger,以确保在一个线程中进行的更改对其他线程可见。

文档中提供了更多信息。

英文:

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.

答案2

得分: 3

  1. Thread 2 在 Thread 1 之前更新了 x。根据你所看到的打印语句的顺序,你无法确定这两个线程之间的执行交错方式。

  2. 确实按照你的预期顺序执行了这两个线程。但是由于 x 不是 volatile,你可能看不到更新后的值。

参考 - https://stackoverflow.com/questions/106591/what-is-the-volatile-keyword-useful-for

英文:

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 - https://stackoverflow.com/questions/106591/what-is-the-volatile-keyword-useful-for

答案3

得分: 2

你不能预测线程的结果。

如果在另一个设备上运行代码或多次运行代码,结果可能会不同。

您不能(或不应该)依赖于时间或调度程序。

我认为并发/非易失性本身可能不是唯一的问题,刷新也是您可能要考虑的因素之一:

x=3(ThreadExample(1))
sysout 3(ThreadExample(1))
syserr x(主线程)
x=1(ThreadExample(2))
sysout 3(ThreadExample(2))
刷新stdout(由jvm退出引起)
刷新stderr(由jvm退出引起)

请注意最后的刷新。stdout和stderr可能不会同步。

这些流被缓冲并随时写入控制台。

虽然写入stdout或stderr的两件事保证以正确的顺序写入,但如果将一件事写入stdout而将另一件事写入stderr,则不能保证这一点。

还保证了在jvm正常终止时(没有kill -9或类似情况),将所有打印到stdoutstderr的内容都会被写入。

如果jvm在写入stdout之前写入stderr,则可以获得结果。

如果您希望正确打印输出,您可能需要做两件事:

  • 在打印后手动调用flush

  • 在操作、printlnflush周围创建一个synchronized块(或类似的块)。 请注意,这可能会损失一些性能/并行性。

如果您想测试刷新是否对您的情况有影响,请在程序末尾添加System.err.flush();(以便在stdout之前刷新stderr)并查看是否有差异。

还有一件事我在其他答案中没有明确找到:JIT优化。

JIT编译器可能会对您的程序进行优化。 例如,它可以将:

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

优化为:

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

因此,即使在调用println时它不是3,也会打印3

英文:

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.

答案4

得分: 1

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.

英文:

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.

答案5

得分: 1

会有3个线程同时运行吗?

是的。第一个线程是主线程,它是启动一切的线程,它调用了您的 public static void main(String args[]) 方法。所有的代码都在一个线程上运行。然后,您的主方法启动了2个线程。因为您开始时有1个线程,所以现在有3个线程。

至于为什么主线程的最终输出是 3,这很难回答,因为存在竞态条件。您有3个线程同时读取一个变量,而其中2个线程在同时更新它,这些操作都发生在并发中。

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

当有3个线程运行时,很容易假设上述的 System.out.println 输出将是 3,但实际情况是,在将其设置为 3 后,另一个线程可能已经更新了它,然后当您打印它时,它已经不再是3了。

还要考虑 volatile 关键字。如果没有它,JVM 可能会在线程内缓存共享值的副本,这可能导致在线程之间读写时出现陈旧性问题。详细信息请参阅:https://stackoverflow.com/questions/106591/what-is-the-volatile-keyword-useful-for

英文:

> 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. https://stackoverflow.com/questions/106591/what-is-the-volatile-keyword-useful-for

答案6

得分: 0

Threads的结果是不可预测的。要确保一致/可预测的行为,使用volatile/Atomic值使更改对其他线程可见。

英文:

The outcome of threads is unpredictable.

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

huangapple
  • 本文由 发表于 2020年8月6日 02:58:37
  • 转载请务必保留本文链接:https://go.coder-hub.com/63271851.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定