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

变量运行器不在循环内更新

屈畅
2023-03-14

像这样,我有两个线程。SleepRunner线程在列表中添加一些随机数,然后将标志更改为true并Hibernate。主线程等待SleepRunner线程,直到SleepRunner对象中的标志从false变为true,然后主线程将中断SleepRunner线程,程序将结束。

但问题是,当主线程中的当循环没有正文代码时,变量“运行者”在循环内没有更新,换句话说,当睡眠运行者线程将标志从假改为真后,程序并没有结束。所以我在想法上尝试使用调试工具,但程序顺利结束。如果我在主线程中的当循环正文中写一些代码,比如System.out.println()或Thread.sleep(1),程序也成功结束了。太不可思议了!有人知道为什么会这样吗?谢谢。

public class Test1 {
        public static void main(String[] args) {
            SleepRunner runner = new SleepRunner();
            Thread thread = new Thread(runner);
            thread.start();
            while(!(runner.isFlag())){
                /*try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }*/
            }
            System.out.println("END");
            thread.interrupt();
        }
    }

public class SleepRunner implements Runnable {
        private boolean flag = false;
    
        public boolean isFlag() {
            return flag;
        }
    
        @Override
        public void run() {
            List<Integer> list = new ArrayList<>();
            for (int i = 0; i < 100; i++) {
                try {
                    Thread.sleep((long) (Math.random() * 200));
                }
                catch (InterruptedException e) {
                    System.out.println("Interrupted");
                }
                int num = (int) (Math.random() * 100);
                System.out.println(Thread.currentThread().getName() + " " + num);
                list.add(num);
            }
    
            flag = true;
    
            System.out.println("30 Seconds");
            try {
                Thread.sleep(30000);
            }
            catch (InterruptedException e) {
                System.out.println("Interrupted in 30 seconds");
            }
            System.out.println("sleep runner thread end");
        }
    }

共有1个答案

毛胜
2023-03-14

你违反了java内存模型

以下是JMM的工作原理*:

每当读取或更新任何字段(来自任何对象)时,每个线程都会抛出一枚硬币。正面,它会复制并据此更新/读取。反面,它不会。你的工作是确保你的代码正确运行,不管硬币是如何着陆的,你不能在单元测试中强制使用coinflip。硬币不需要“公平”。硬币的行为取决于你的音乐播放器中播放的音乐、幼儿的突发奇想和月亮的相位。(换句话说,任何更新/读取都可以对本地缓存副本进行,也可以不进行,直到java实现)。

你可以有把握地得出结论,正确操作的唯一方法是确保线不会翻转硬币。

实现这一目标的方法是建立所谓的“先到先得”关系。建立它们主要通过使用同步原语或调用使用同步原语的方法来完成。例如,如果我这样做:

线程X:

synchronized(x) {
    x.foo();
    System.out.println(shared.y);
    shared.y = 10;
}

线程Y:

synchronized(x) {
    x.foo();
    System.out.println(shared.y);
    shared.y = 20;
}

然后你就建立了一种关系:代码块a排在代码块B之前,反之亦然,但你至少已经确定它们必须按顺序运行。

因此,这将保证打印0 100 20。如果没有同步块,它也可以合法打印0。这三个结果都是可以接受的结果(java lang规范说这没问题,任何你认为没有意义的bug都会被忽略为“按预期工作”)。

易失性也可以使用,但易失性相当有限。

一般来说,由于无法对其进行充分测试,在java中只有3种方法可以正确执行线程:

  1. “大型”:使用Web服务器或其他处理多线程的应用程序框架。您不需要编写psv main()方法,框架需要编写,您只需要编写“处理程序”。您的处理程序中没有任何一个会接触任何共享数据。处理程序要么不共享数据,要么通过设计用于正确操作的总线共享数据,例如可串行事务隔离模式下的DB,或者rabbitmq或其他一些消息总线
  2. “在小范围内”:使用fork/join并行化大任务。当然,任务的处理程序不能使用任何共享数据
  3. 阅读《实践中的并发》(the book),更喜欢使用java中的类。util。并发包,并且通常是这方面工作的专家,因为以任何其他方式进行线程处理都可能会导致编程错误,而您的测试可能无法捕获这些错误,但它们会在生产时爆炸,或者不会导致实际的多线程处理(例如,如果你过于热心地同步所有内容,那么除了一个核心之外,你的所有核心都在等待,而你的代码实际上会比单线程运行慢得多)

*)完整的解释是关于一本书的价值。我只是给你们一个过于简单的亮点,因为这只是一个一般的答案。

 类似资料:
  • 问题内容: 为什么以下工作正常? 但是据说这是危险的/不正确的: 是否需要在循环外声明变量? 问题答案: 局部变量的范围应始终尽可能小。 在你的例子我相信是不会使用的外while循环,否则你就不会问这个问题,因为它声明的内部while循环不会是一个选项,因为它不会编译。 所以,既然是不使用外循环,在尽可能小的范围是内 while循环。 所以,答案是着重那绝对应该被while循环内声明。没有,没有,

  • 我正在研究一个涉及for循环和if-else语句的java函数。我需要根据多次迭代中的条件更改标志变量的值。我声明了一个名为flag的变量,并希望根据每次迭代中的条件进行更改。我需要在每次迭代结束时打印flag变量的值。但是当我打印变量时,它显示了一个错误,变量没有初始化。如果我给它一个初始值,它会一直打印初始值,而不是在If-else语句中处理的值。我不能根据自己的要求初始化for循环中的fla

  • 问题内容: java中是否允许这样做: 我的问题的关键词是。是否可以允许最终变量随循环的每次运行而变化?我想知道这是因为final说您不能更改变量的值(仅调用),但是我正在使用重新定义整个变量。 它们是两个完全不同的变量,只是具有相同的名称-循环的前一次运行中的变量已经朝着垃圾收集器的方向前进了吗? 问题答案: 是的,允许。该关键字意味着你不能改变的变量的值 的范围之内 。对于循环示例,您可以认为

  • 如下所示,我要反转数组。但我的代码不管用。这是我的for循环。请看一下。这个for循环正确吗。代码如下。

  • 循环变量内建函数从 FreeMarker 2.3.23 版本开始存在。 这些内建函数只能用于list 和 items 指令 的循环变量 (也可以用于已经废弃的 foreach 指令)。 下面是一些说明(loopVar?index 在可以列表的值中进行迭代,返回从0开始的索引): <#-- Note: x is a loop variable --> <#list ['a', 'b', 'c'] a

  • 问题内容: 我相信在AS3中,您应该在循环外初始化所有变量,以提高性能。JavaScript也是如此吗?哪个更好/更快/最佳实践? 要么 问题答案: 有 绝对没有区别 意义还是性能,在JavaScript或ActionScript。 是解析器的指令,而 不是 运行时执行的命令。如果在函数体(*)中的任何位置一次或多次声明了特定的标识符,则该标识符在块中的所有使用将引用局部变量。声明是在循环内部,循