gpt4 book ai didi

java - 当 System.out.println 出现在临界区时,多个线程显示异常值

转载 作者:搜寻专家 更新时间:2023-11-01 02:20:37 25 4
gpt4 key购买 nike

我有以下代码:

public class MyApp {
public static void main(String[] args) throws InterruptedException {
SharedResource sharedResource = new SharedResource();

Runnable first = () -> {
sharedResource.increment(10000);
};

Runnable second = () -> {
sharedResource.increment(10000);
};

Thread thread = new Thread(first, "FirstThread");
Thread thread2 = new Thread(second, "SecondThread");
thread.start();
thread2.start();
thread.join();
thread2.join();
System.out.println("The value of counter is " + sharedResource.getCounter());
}
}

使用这个类:

public class SharedResource {
private int counter;

public void increment(int times) {
for (int x=1; x<=times;x++) {
counter++;
// System.out.println(counter);
}
}

public void decrement() {
counter--;
}

public int getCounter() {
return counter;
}
}

我很好奇为什么这种情况总是发生。

System.out.println()去掉自增方法后,总值

System.out.println("The value of counter is " + sharedResource.getCounter());

是随机的 - 这是异常(exception),因为多个线程共享相同的 counter

然而,当 System.out.println(counter); 出现在 increment 方法上时,代码似乎不再存在多线程问题。

counter 的最终结果始终为 20,000,因为代码从每个线程中循环 10,000 次而除外。谁能解释一下为什么会这样?

最佳答案

这是由于比赛窗口非常小。

默认系统输出是线程安全的 PrintStream:

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

所以基本上线程执行以下操作:

  1. 增量计数器(~几十纳秒)
  2. 等待前一个线程释放锁,获取它并打印到控制台(~毫秒,慢 1000 倍)
  3. 转到 1

当你的关键部分比非关键部分长 1000 倍时,你的线程基本上在其上序列化并且计数器更新重叠的概率变得非常小,系统输出没有什么特别的.

证明方式:

  1. 您可以编写非线程安全的 PrintStream 实现:

    public class NullPrintStream extends PrintStream {
    public NullPrintStream() {
    // Utility constant from apache-commons
    super(NullOutputStream.NULL_OUTPUT_STREAM);
    }

    @Override
    public void println(int x) {
    // No synchronization here
    }
    }

然后通过 System.setOut(new NullPrintStream()) 将其设置为系统输出,结果将再次开始 Swing 。

  1. 为了在开始时提供更大的比赛窗口,您可以在闩锁上同步您的可运行对象,以便它们几乎同时开始:

    CountDownLatch latch = new CountDownLatch(1);
    Runnable first = () -> {
    try {
    latch.await();
    sharedResource.increment(10000);
    }
    catch (Exception e) {
    }
    };
    // ... start your threads here
    latch.countDown();

然后,如果您多次运行此示例,您将看到类似的内容(请注意,我将其打印到 System.err,因为我已经覆盖了 System.out)

计数器的值为20000

计数器的值为19996

计数器的值为19994

计数器的值为19999

计数器的值为20000

关于java - 当 System.out.println 出现在临界区时,多个线程显示异常值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45267753/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com