【问题标题】:Java 6 Threading output is not Asynchronous?Java 6 线程输出不是异步的?
【发布时间】:2010-07-23 13:30:28
【问题描述】:

此代码应该产生均匀和不均匀的输出,因为在任何方法上都没有同步。然而,我的 JVM 上的输出总是均匀的。我真的很困惑,因为这个例子直接来自 Doug Lea。

public class TestMethod implements Runnable {

private int index = 0;

    public void testThisMethod() {
        index++;
        index++;
        System.out.println(Thread.currentThread().toString() + " "
                    + index );

    }

    public void run() {
        while(true) {
            this.testThisMethod();
        }
    }

    public static void main(String args[]) {
        int i = 0;
        TestMethod method = new TestMethod();
        while(i < 20) {
            new Thread(method).start();
            i++;
        }
    }
}

输出

线程[Thread-8,5,main] 135134

线程[Thread-8,5,main] 135136

线程[Thread-8,5,main] 135138

线程[Thread-8,5,main] 135140

线程[Thread-8,5,main] 135142

线程[Thread-8,5,main] 135144

【问题讨论】:

    标签: java multithreading asynchronous


    【解决方案1】:

    我尝试使用volatile 并得到以下结果(if 仅在奇数时打印):

    Thread[Thread-12,5,main] 122229779
    Thread[Thread-12,5,main] 122229781
    Thread[Thread-12,5,main] 122229783
    Thread[Thread-12,5,main] 122229785
    Thread[Thread-12,5,main] 122229787
    

    对cmets的回答:

    索引实际上是共享的,因为我们有一个 TestMethod 实例,但许多 Threads 在我们拥有的一个 TestMethod 上调用 testThisMethod()


    代码(除上述之外没有变化):

    public class TestMethod implements Runnable {
    
        volatile private int index = 0;
    
            public void testThisMethod() {
                index++;
                index++;
                if(index % 2 != 0){
                System.out.println(Thread.currentThread().toString() + " "
                            + index );
                }
    
            }
    
            public void run() {
                while(true) {
                    this.testThisMethod();
                }
            }
    
            public static void main(String args[]) {
                int i = 0;
                TestMethod method = new TestMethod();
                while(i < 20) {
                    new Thread(method).start();
                    i++;
                }
            }
        }
    

    【讨论】:

    • if(index % 2 > 0) System.out.println(Thread.currentThread().toString() + " " + index );此代码打印奇数输出,但没有它,我得到的只是偶数输出,这有多奇怪?
    • @Excalibur:这可能是因为System.out.println() 需要获取共享资源,因此会影响线程调度。
    【解决方案2】:

    首先:正如其他人所指出的,根本无法保证您的线程在两个增量操作之间会被中断。

    请注意,打印到System.out 很可能会在您的线程上强制进行某种同步,因此当您的线程从该时间片返回时很可能刚刚开始一个时间片,因此它们可能会完成两个增量操作和然后等待System.out的共享资源。

    尝试将System.out.println() 替换为以下内容:

    int snapshot = index;
    if (snapshot % 2 != 0) {
      System.out.println("Oh noes! " + snapshot);
    }
    

    【讨论】:

    • 经过几次实验,我想这就是答案。您是否碰巧知道为什么 print 在其中一个线程中的第一次 index++ 增量之后永远不会被调用?
    • 我认为事情的真相是你在这个问题上任由JVM作者摆布。
    【解决方案3】:

    你不知道。自动调度的重点是 它不做任何保证。它可能会完全不同地对待运行相同代码的两个线程。或者完全一样。或者一个小时完全一样,然后突然不同......

    关键是,即使您解决了其他答案中提到的问题,您仍然不能依赖特定方式出现的事情;您必须始终为 Java 内存和线程模型允许的任何可能的交错做好准备,这包括println 总是在偶数增量之后发生的可能性,即使 从表面上看,你似乎不太可能。

    【讨论】:

    • 同步被高估了.. ;) 不,说真的.. 我完全同意你的帖子!
    • 但是线程应该在这里干扰并且有时会给我不均匀的计数。至少我是这么理解的。调度器不控制对象上的锁?
    • 它们可能会干扰,但这不在您和 JVM 之间的合同中。如果您需要确保它不会发生,那么是的,您需要同步。但这并不意味着如果你不同步,java 保证交错这些语句,它只保证它 如果你 同步交错。你不能(轻易地)激发你想要展示的实际交错行为;您需要了解很多关于 JVM 的内部状态、环境等的信息,即使如此,行为仍然可以随时更改。
    【解决方案4】:

    结果完全符合我的预期。 index 在输出之间增加了两次,线程之间没有交互。

    扭转这个问题 - 你为什么会期望奇怪的输出?

    编辑:哎呀。我错误地认为每个线程都创建了一个新的可运行对象,因此每个线程都有一个不同的索引,而不是共享的。令人不安的是,这样一个有缺陷的答案是如何获得 3 个赞成票的......

    【讨论】:

    • 我需要一个同步方法来保护执行对象上的信号量。否则多个线程在该方法上并发执行。
    • 另一个线程的输出可以在第二个“索引”增量之前出现。有一个可运行对象和多个线程在该可运行对象上运行。有点混乱。
    • 交互是一个共享的index变量。
    【解决方案5】:

    您尚未将index 标记为易失性。这意味着允许编译器优化对它的访问,它可能会将您的 2 个增量合并为一个加法。

    【讨论】:

    • 试过 volatile private int index = 0;没有改变任何东西。
    【解决方案6】:

    你得到你启动的第一个线程的输出,因为这个线程循环并且没有给其他线程运行的机会。

    所以你应该在循环中使用 Thread.sleep() 或(不推荐)Thread.yield()。

    【讨论】:

    • 这似乎也是真的。 Sleeping in the Thread 似乎引发了正确的行为 - 输出是奇数和偶数。好像你必须给其他线程一个执行的机会。
    • 错了。所有线程都有机会运行。一个线程中的无限循环不会迫使其他线程永远等待。
    • 这实际上以最少的代码修改工作。我也纳闷。我原以为其他线程不会饿死,但似乎是这样。
    • 所有线程都运行的证据来自这样一个事实:将 if 语句放入会捕获所有奇数输出。我必须和约阿希姆一起去这个。输出只是因对 System.out 的同步访问而存在缺陷。
    • 错了。输出日志中的证据:线程号,currentThread() 返回对当前正在执行的线程对象的引用,该对象并不总是同一个。
    猜你喜欢
    • 2011-12-07
    • 2020-12-29
    • 1970-01-01
    • 2010-09-08
    • 2015-01-06
    • 1970-01-01
    • 1970-01-01
    • 2012-04-28
    • 2012-11-22
    相关资源
    最近更新 更多