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

在循环中执行remainder操作的Java线程阻塞所有其他线程

储仲渊
2023-03-14

下面的代码片段执行两个线程,一个是每秒记录一次的简单计时器,第二个是执行remainder操作的无限循环:

public class TestBlockingThread {
    private static final Logger LOGGER = LoggerFactory.getLogger(TestBlockingThread.class);

    public static final void main(String[] args) throws InterruptedException {
        Runnable task = () -> {
            int i = 0;
            while (true) {
                i++;
                if (i != 0) {
                    boolean b = 1 % i == 0;
                }
            }
        };

        new Thread(new LogTimer()).start();
        Thread.sleep(2000);
        new Thread(task).start();
    }

    public static class LogTimer implements Runnable {
        @Override
        public void run() {
            while (true) {
                long start = System.currentTimeMillis();
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // do nothing
                }
                LOGGER.info("timeElapsed={}", System.currentTimeMillis() - start);
            }
        }
    }
}

这给出了以下结果:

[Thread-0] INFO  c.m.c.concurrent.TestBlockingThread - timeElapsed=1004
[Thread-0] INFO  c.m.c.concurrent.TestBlockingThread - timeElapsed=1003
[Thread-0] INFO  c.m.c.concurrent.TestBlockingThread - timeElapsed=13331
[Thread-0] INFO  c.m.c.concurrent.TestBlockingThread - timeElapsed=1006
[Thread-0] INFO  c.m.c.concurrent.TestBlockingThread - timeElapsed=1003
[Thread-0] INFO  c.m.c.concurrent.TestBlockingThread - timeElapsed=1004
[Thread-0] INFO  c.m.c.concurrent.TestBlockingThread - timeElapsed=1004

共有1个答案

卫梓
2023-03-14

在这里的所有解释之后(感谢Peter Lawrey),我们发现这种暂停的主要原因是很少到达循环内的安全点,因此需要很长时间来停止所有线程进行JIT编译的代码替换。

但我决定更深入地研究为什么安全点很少被到达。我发现为什么while循环的后跳在本例中不是“安全的”,这有点令人困惑。

所以我召唤-xx:+printassembly来帮忙

-XX:+UnlockDiagnosticVMOptions \
-XX:+TraceClassLoading \
-XX:+DebugNonSafepoints \
-XX:+PrintCompilation \
-XX:+PrintGCDetails \
-XX:+PrintStubCode \
-XX:+PrintAssembly \
-XX:PrintAssemblyOptions=-Mintel

在分析阶段,从来没有看到变量i等于0。这就是为什么C2推测性地优化了这个分支,以便将循环转换为类似

for (int i = OSR_value; i != 0; i++) {
    if (1 % i == 0) {
        uncommon_trap();
    }
}
uncommon_trap();

注意,原来的无限循环被重塑为一个有计数器的规则有限循环!由于JIT优化以消除有限计数循环中的安全点轮询,因此此循环中也没有安全点轮询。

过了一段时间,i包装回0,这样就找到了不常见的陷阱。该方法被取消优化,并在解释器中继续执行。在使用新知识重新编译时,C2识别了无限循环并放弃编译。该方法的其余部分在解释器中进行,并有适当的安全点。

也可以通过手动添加safepoint重写代码。例如,thread.yield()循环结束时的调用,甚至将int i更改为long i(谢谢,Nitsan Wakart)也将修复暂停。

 类似资料:
  • 我有4-5个工作线程处理大型消息队列。我还有另一段代码,它使用2-3个worker运行。我想在处理大型消息队列时阻止所有其他工作者。 我正在使用JDK6和Jms 编辑: 队列进程工作者从未终止。当没有消息时,它们阻塞队列。这些工作者由执行器线程池管理,如果我使用读写锁,其中一个工作者也会被阻塞。此外,如果使用循环屏障,那么我必须终止线程,以便重新传递阻塞的第二个进程。由于工作者是由线程池管理的,所

  • 问题内容: Node.JS的最大优点是它具有非阻塞性。它是单线程的,因此不需要为每个新的传入连接生成新的线程。 在事件循环(实际上是单线程)的后面,有一个“非阻塞工作程序”。这个东西不再是单线程的,所以(据我了解),它可以为每个任务产生一个新线程。 也许我误会了一些东西,但是优势到底在哪里。如果要处理的任务很多,那么“非阻塞工作”会不会变成“阻塞工作人员”? 谢谢克里斯蒂安 问题答案: 您需要阅读

  • 问题内容: 我有一个应用程序,在某些情况下需要计算某些次数。此计算功能具有@Async注释(来自Spring Framework),可以在4个线程上运行这些计算。问题是我需要大约40000个这些计算,并且我想知道所有计算的开始和结束之间的时间,因此我看到在调用计算函数的for循环之前和之后的时间是什么。但是现在所有计算都放入队列中,因此for循环立即完成,时间大约为1秒,而计算要花几个小时才能完成

  • 线程实例的join()方法可用于将一个线程的执行开始“连接”到另一个线程的执行结束,这样一个线程在另一个线程结束之前不会开始运行。如果对线程实例调用join(),则当前运行的线程将阻塞,直到线程实例完成执行 但是如果我有多个线程并且当我在循环内部调用join时。所有线程并行运行。但是根据连接的概念,首先连接的线程应该完成,然后只有主线程才允许连接其他线程。 } 在上面的代码中,如果第一个线程被连接

  • 注意:我工作了很多时间并研究了google和stackoverflow,但我找不到答案。 我用线。sleep(),它冻结了所有其他JDialog、JFrame和线程。 我的示例代码: 在这种情况下,JDialog无法正确显示: inccorect出现jdialog 但它必须符合这一点: true出现jdialog 我怎样才能解决这个问题。我想让主线程等待另一个线程。有人可以纠正我的示例代码,或者在

  • 夸克有一件事我很难理解。我在Oracle中使用JPA。因此,我有错误IllegalStateExcema:您试图在IO线程上执行阻塞操作。这是不允许的,因为阻塞了IO线程,我在QUUKUS文档中查看了如何在没有这个困难的情况下进行JPA调用。但是所有的例子和文档都使用PostgreSQL或MariaDB与响应客户端。但是我没有找到任何用于经典JDBC客户端的客户端。 我找到了一个部分有效的解决方案