像这样,我有两条线。sleeprunner线程向列表中添加一些随机数,然后将flag更改为true并休眠。主线程等待sleeprunner线程,直到sleeprunner对象中的标志从false变为true,然后主线程将中断sleeprunner线程,程序将结束。
但问题是,当while循环在主线程中没有主体代码时,变量runner不会在循环内更新,换句话说,sleeprunner线程将标志从false更改为true后,程序不会结束。所以我试着在idea中使用调试工具,但是程序结束得很顺利。如果我在主线程的while循环体中编写一些代码,比如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条答案
按热度按时间uqzxnwby1#
你违反了java内存模型。
以下是jmm的工作原理*:
每当读取或更新任何字段(来自任何对象)时,每个线程都会抛出一枚硬币。在头部,它将复制并更新/读取。在尾巴上,它不会。您的工作是确保您的代码正确运行,而不管coinflip是如何到达的,并且您不能在单元测试中强制coinflip。硬币不一定是“公平的”。硬币的行为取决于音乐播放器中播放的音乐、幼儿的突发奇想和月亮的相位(换句话说,任何更新/读取都可以对本地缓存副本执行,也可以不执行,直到java实现)。
你可以有把握地得出这样的结论:唯一正确的方法是确保线不会翻转硬币。
实现这一目标的方法是建立所谓的“先到先得”关系。建立它们主要是通过使用同步原语,或者通过调用使用同步原语的方法来完成的。例如,如果我这样做:
螺纹x:
螺纹y:
然后你建立了一个关系:代码块a在代码块b之前,反之亦然,但你至少建立了它们必须按顺序运行。
因此,这将打印
0 10
或者0 20
,保证。没有同步块,它可以合法打印0 0
也。所有3个结果都是可以接受的结果(javalang规范说没问题,任何你认为没有意义的bug都会被忽略为“按预期工作”)。volatile
也可以使用,但挥发性相当有限。一般来说,由于这无法得到充分的测试,所以在java中只有3种方法可以正确地执行线程:
“in the large”:使用Web服务器或其他处理多线程的应用程序框架。你不写
psv main()
方法,该框架会这样做,而您编写的只是“处理程序”。你的处理程序根本就不涉及任何共享数据。处理程序要么不共享数据,要么通过一个设计好的总线来共享数据,比如在可序列化事务隔离模式下的db,或者rabbitmq或其他一些消息总线。“in the small”:使用fork/join并行化大型任务。当然,任务的处理程序不能使用任何共享数据。
阅读《实践中的并发》(本书),更喜欢使用java.util.concurrent包中的类,并且通常是这方面工作的Maven,因为以任何其他方式执行线程都可能导致编程错误,而您的测试可能不会发现这些错误,但会在生产时爆炸,或者将不会导致实际的多线程处理(例如,如果您过于热心地同步所有内容,那么除了一个核心之外,您的所有核心都会等待,并且您的代码实际上会比单线程运行慢得多)。