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

代码在同一线程上无序运行

邹铭
2023-03-14

我们都知道Java会彻底优化我们的代码,我们都喜欢它。嗯,大多数时候。下面是一段让我头疼的代码:

public class BrokenOptimizationTest {

/**
 * This thread constantly polls another thread object's private field.
 */
public static class ComparingThread extends Thread {
    private int currentValue = 0;
    private AdditionThread otherThread = null;

    public ComparingThread(AdditionThread add) {
        this.otherThread = add;
    }

    @Override
    public void run() {
        while (true) {
            int testValue = currentValue;

            if (BrokenOptimizationTest.shouldDoSomething) {

                do {
                    testValue = otherThread.getValue();
                    BrokenOptimizationTest.doSomething();
                    // System.out.println(testValue); // to see testValue really changes
                }
                while (testValue == currentValue);

            }
            else {

                do {
                    testValue = otherThread.getValue();
                    // System.out.println(testValue); // to see testValue really changes
                }
                while (testValue == currentValue);

            }

            System.out.println("{ testValue: " + testValue + ", currentValue: " + currentValue + " }");

            currentValue = testValue;
        }
    }
}

/**
 * This thread often adds to its pollable value.
 */
public static class AdditionThread extends Thread {
    private int currentValue = 0;
    public long queryCount = 0;

    public int getValue() {
        ++queryCount;
        return currentValue;
    }

    @Override
    public void run() {
        while (true) {
            ++currentValue;

            //I said 'often', so sleep some more
            try {
                Thread.sleep(1);
            }
            catch (InterruptedException e) {}
        }
    }
}

/**
 * Whether or not the program must simulate doing an expensive calculation between consecutive queries.
 */
public static boolean shouldDoSomething = false;

/**
 * Simulates doing an expensive calculation
 */
public static void doSomething() {
    try {
        Thread.sleep(0, 100);
    }
    catch (InterruptedException e) {}
}


/**
 * Call the program with something like "slow" to enable doSomething
 */
public static void main(String[] args) {
    if (args.length >= 1 && (args[0].toLowerCase().contains("slow") || args[0].toLowerCase().contains("dosomething")))
        shouldDoSomething = true;


    AdditionThread addThread = new AdditionThread();
    ComparingThread compThread = new ComparingThread(addThread);
    addThread.start();
    compThread.start();

    /**
     * Print the current program state every now and then.
     */
    while (true) {
        System.out.println("{ currentValue: " + addThread.getValue() + ", activeThreads: " + Thread.activeCount() + ", queryCount: " + addThread.queryCount + " }");
        System.out.flush();

        try {
            Thread.sleep(1000);
        }
        catch (InterruptedException e) {}
    }
}
}

在快速、慢速的单线程和多html" target="_blank">线程处理器之间,结果可能会有所不同。在我测试的计算机上(没有doSomething),输出如下:

{ currentValue: 1, activeThreads: 3, queryCount: 1 }
{ testValue: 1, currentValue: 0 }
{ testValue: 2, currentValue: 1 }
{ testValue: 3, currentValue: 2 }
{ testValue: 4, currentValue: 3 }
{ testValue: 5, currentValue: 4 }
{ testValue: 6, currentValue: 5 }
{ testValue: 7, currentValue: 6 }
{ testValue: 8, currentValue: 7 }
{ testValue: 9, currentValue: 8 }
{ testValue: 10, currentValue: 9 }
{ testValue: 11, currentValue: 10 }
{ testValue: 12, currentValue: 11 }
{ testValue: 13, currentValue: 12 }
{ currentValue: 994, activeThreads: 3, queryCount: 2176924819 }
{ currentValue: 1987, activeThreads: 3, queryCount: 4333727079 }
{ currentValue: 2980, activeThreads: 3, queryCount: 6530688815 }
{ currentValue: 3971, activeThreads: 3, queryCount: 8723797559 }

CompareThread的前几次迭代运行良好,然后Java进行了“优化”:testValue和currentValue总是相等的,并且不断地改变它们的值,尽管线程从未离开最内层的循环。我能想到的唯一原因是Java的执行顺序不对,就像这样:

do {
    testValue = otherThread.getValue();
    currentValue = testValue; // moved up from beneath the loop
}
while (testValue == currentValue);

我知道Java编译器中允许无序执行,因为它可以提高性能,但这些语句显然是相互依赖的。

我的问题很简单:为什么?为什么 Java 以这种方式运行程序?

注意:如果程序以参数doSomething启动,或者如果AdditionThread.currentValue变得不稳定,代码运行得很好。

共有1个答案

柳业
2023-03-14

你已经回答了自己的问题:

如果加法线程值变得不稳定,则代码运行良好。

Java 内存模型不保证当您从“比较线程”内部读取“中的”加法线程“值时,您将看到”附加线程“中存在的最新版本。如果数据对其他线程可见,则必须使用提供的工具之一,易失性、同步的 java.util.concurrent.*,以便告诉系统您需要可见性保证。

无序执行并不是导致意外行为的优化,只是ComparingThread保留了AdditionThread的副本。自身堆栈上的currentValue。

打开“doSomething”也可以解决这个问题,因为将线程置于睡眠状态通常会导致它们在醒来时刷新堆栈,尽管这并没有得到正式的保证。

 类似资料:
  • 问题内容: 这是一个普遍的Java问题,而不是Android的第一个问题! 我想从二级线程的上下文中了解如何在主线程上运行代码。例如: 这类事情-我意识到我的示例有点差,因为在Java中,您不需要进入主线程即可打印出某些内容,并且Swing也具有事件队列- 但在一般情况下,您可能需要在后台线程的上下文中,在主线程上运行Runnable。 编辑:为了进行比较-这是我在Objective-C中的做法:

  • 在一个android服务中,我创建了用于执行一些后台任务的线程。 我遇到一个情况,线程需要在主线程的消息队列上发布特定任务,例如。 有没有方法获取主线程的并从我的另一个线程向它发布/?

  • 我有一个在多个线程上创建可运行对象(来自同一类的实例)的程序,每个可运行对象包含一个使用发布事件和@EventListener接收事件的对象。 我想做的是工作线程上的接收器只接收从该线程发布的事件。 由于多个线程上的对象中的方法签名是相同的,所以不确定是否从一个线程发布了一个事件,那么所有线程都将收到相同的事件更改?如果是这样,那么如何确保工作线程只接收在该线程上发布的事件?

  • 问题内容: codepad.org允许您在线运行C,C ++,D等代码,但不能运行Java …我可以使用Java的网站吗? 问题答案: 还有http://ideone.com/(支持多种语言)

  • 我想让一些代码在后台持续运行。我不想在服务中这样做。还有其他可能的方法吗? 我曾尝试在我的活动中调用线程类,但我的活动在后台保留了一段时间,然后就停止了。线程类也停止工作。