当前位置: 首页 > 知识库问答 >
问题:

与System.out [duplicate]关联的Java线程的奇怪行为

习华灿
2023-03-14

我有一个简单的TestThreadClientMode类来测试竞争条件。我尝试了两次:

  1. 当我使用System.out运行以下代码时。println(计数)在第二个线程中注释,输出为:

操作系统:Windows 8.1标志完成设置为真…

第二个线程永远活着。因为第二个线程永远看不到主线程设置为true的完成标志的变化。

当我取消注释< code>System.out.println(count)时;输出是:

OS:Windows 8.1 0…190785 190786标志完成设置为true完成!线程-0为真

程序在1秒后停止。

系统.out.println(计数); 如何使第二个线程看到所做的更改完成

public class TestThreadClientMode {
    private static boolean done;
    public static void main(String[] args) throws InterruptedException {
        new Thread(new Runnable() {
            public void run() {
                int count = 0;
                while (!done) {
                    count ++;
                    //System.out.println(count);
                }
                System.out.println("Done! " + Thread.currentThread().getName() + "  " + done);
            }
        }).start();
        System.out.println("OS: " + System.getProperty("os.name"));

        Thread.sleep(1000);
        done = true;

        System.out.println("flag done set true ");
    }
}

共有3个答案

怀刚毅
2023-03-14

println() 实现包含显式内存屏障:

public void println(String x) {
    synchronized (this) {
        print(x);
        newLine();
    }
}

这导致调用线程刷新所有变量

以下代码将具有与您的代码相同的行为:

    public void run() {
        int count = 0;
        while (!done) {
            count++;
            synchronized (this) {
            }
        }
        System.out.println("Done! " + Thread.currentThread().getName() + "  " + done);
    }

事实上,任何对象都可以用于监视器,以下内容也适用:

synchronized ("".intern()) {
}

创建显式内存屏障的另一种方法是使用易失性,因此以下方法将起作用:

new Thread() {
    private volatile int explicitMemoryBarrier;
    public void run() {
        int count = 0;
        while (!done) {
            count++;
            explicitMemoryBarrier = 0;
        }
        System.out.println("Done! " + Thread.currentThread().getName() + "  " + done);
    }
}.start();
鲁斯伯
2023-03-14

System.out.println(count)是怎么做到的;让第二个线程看到< code>done中的变化?

您正在见证打印的副作用;您的程序正在遭受并发争用情况的困扰。在 CPU 之间协调数据时,告诉 Java 程序您希望在 CPU 之间共享数据非常重要,否则 CPU 可以自由地延迟彼此之间的通信。

有几种方法可以在 Java 中执行此操作。主要的两个是关键字“易失性”和“同步”,它们都将硬件人员称为“内存障碍”的东西插入到代码中。如果不在代码中插入“内存屏障”,则不会定义并发程序的行为。也就是说,我们不知道“完成”何时对其他CPU可见,因此它是一个竞争条件。

这是系统输出的实现;注意使用同步。synchroniz 关键字负责在生成的汇编程序中放置内存屏障,这会产生使变量“完成”对其他 CPU 可见的副作用。

public void println(boolean x) {
    synchronized (this) {
        print(x);
        newLine();
    }
}

正确的程序修复方法是,在读取完成时放置一个读内存屏障,在写入时放置写内存屏障。通常,这是通过从同步块中读取或写入“done”来完成的。在这种情况下,将变量done标记为volatile将产生相同的净效果。您还可以对变量使用AtomicBoolean而不是boolean

百里景山
2023-03-14

这是内存一致性错误的一个很好的例子。简单地说,变量被更新了,但第一个线程并不总是看到变量发生了变化。这个问题可以通过如下声明使完成变量易失性来解决:

private static volatile boolean done;

在这种情况下,对变量的更改对所有线程都可见,程序总是在一秒钟后终止。

更新:似乎使用System.out。println确实解决了内存一致性问题,这是因为print函数使用了实现同步的底层流。同步建立了我链接的教程中描述的“先发生后发生”关系,其效果与volatile变量相同。(此答案的详细信息。还感谢@Chris K指出了流操作的副作用。)

 类似资料:
  • 好的,最近我在寻找一种在PowerShell中实现空合并的方法,我遇到了这篇文章:在PowerShells中实现空聚合。 我看到了@Zenexer的评论,很感兴趣。语法如下: 这非常有效。然而,我的一位同事(沃尔特·帕克特)和我非常感兴趣,对语法做了更多的挖掘,发现了一些真正的奇怪之处。 在我进入怪异之前,任何人都可以指出任何解释这种语法的文档吗? 经验教训: < li >测试应该进入数组的最后一

  • 所以我更新了代码,添加了行所做的是将主线程置于Hibernate状态一段时间,因此jvm可以获得一些时间来创建一个新线程。我正在得到我的预期输出

  • 我有一个ui显示的数据表:repeat。因为我希望用户能够在每行的基础上更改数据,所以每行都包含在一个h:form中。最后,每个h:form都有一个带有f:ajax标记的按钮。我的行为越来越不一致。 上述方法可行,但带宽显然不便宜。 如果我将render=“@all”更改为render=“@form”,Firebug显示发送的部分响应正常,但我的浏览器(Firefox)神秘地没有显示它。所以我猜它

  • 问题内容: 我有以下简单代码: 正如我的python知识所期望的那样,输出为3-整个列表将包含的最后一个值。但是,这在内部如何运作? AFAIK,python变量只是对对象的引用,因此第一个闭包必须包含对象的第一个引用-并且此对象肯定是1,而不是3 O_O。python闭包如何将变量本身而不是对象作为变量引用的对象?它是否将变量名另存为纯文本,一些“对变量的引用”或什么? 问题答案: 闭包不是指

  • 我发现了Java并发的奇怪行为。请参阅下面的下一段代码: 在我看来,这段代码应该挂起并永远等待,但是在控制台中的next out没有任何问题地完成了代码: 我试图找到一些关于如果线程死了通知锁的信息,但缺乏。我也没有在Java规范中找到任何信息。 但是如果我试图锁定其他对象(而不是thread对象),就会像我预期的那样工作得很好。

  • 如果有人理解java编译器为什么这么做,请解释一下。