这个ThreadPoolExecutor需要同步吗?

问题描述 投票:0回答:1

给定配置的执行者:

ThreadPoolExecutor executor = new ThreadPoolExecutor(
                1, // corePoolSize
                1, // maximumPoolSize
                0, // keepAliveTime
                TimeUnit.MILLISECONDS, // TimeUnit
                new LinkedBlockingQueue<>() // Task queue
        );
        

将保持单个线程处于活动状态,而执行的任何额外任务将被添加到 FIFO LinkedBlockingQueue 中。

根据源代码的内容,这个工作线程中发生的一切都是顺序的。

创建并插入到队列中的可运行堆栈帧仍然会受到正常内存排序效果的影响,因此加载和存储仍然需要栅栏,但是...

在实践中,如果一切都是顺序的,我似乎很清楚,不会发生竞争条件,因此唯一的问题似乎是线程缓存,也称为内存提升和负载简化(用于双重检查)

但是...如果可运行堆栈帧...作为其“独特范围”的一部分出现...

Runnable toExecuteA = () -> {
   // code...
};
Runnable toExecuteB = () -> {
   // code...
};

在我看来,它们的重新排序只会发生在“{}”...框架的范围内。

由于编译时不知道执行 A 时所有 Runnable 的顺序,而 B 永远不会交错,并且由于使用相同的线程,处理器也不会交错该序列。

所以...

  • a) 无需同步 => 一切都会按顺序进行。

  • b) 重新排序仅限于可运行帧的范围内。

  • c) 负载可以被提升和简化,所以 memory_order_relaxed 就像 需要栅栏来保留 Runnable 堆栈帧 PO。

  • d) 由于在所有 Runnable 中强制执行顺序,因此没有“获取” 需要像栅栏一样的“释放”。

现在...即使处理器对所有调用进行虚拟化,并设法将序列 toExecuteA + toExecuteB + toExecuteC 等排序...序列...变成单个不间断序列...处理器可能会进行的简化和省略如果事件循环已经完成处理,之后不会影响可能仍然发生的最终结果。

唯一的问题是,在到达可运行序列中的最后一行之前,该处理器的核心是否与同一处理器的另一个核心进行交互。

我的观察正确吗?

java multithreading compilation event-loop
1个回答
0
投票

唯一的问题是,在到达可运行序列中的最后一行之前,该处理器的核心是否与同一处理器的另一个核心进行交互。

“核心”不是 Java 概念。

...可运行的堆栈帧...

“栈帧”也不是 Java 概念。

唯一重要的是有多个线程。 Java 语言规范 是有关线程之间交互问题的最终权威。

你的程序(我们能看到的很少)有两个线程;它有执行器的工作线程,并且有创建执行器的线程。我们称其为“主线程”。

worker 将对各种对象依次执行一系列

run()
函数调用。这些不同的函数激活之间的交互与单线程程序执行相同的调用序列时的交互没有什么不同。它实际上只是一个线程来执行此操作,如果操作系统选择将线程从一个处理器移动到另一个处理器,则操作系统有责任确保这对于您的程序透明地发生。这不是你需要担心的事情。 对于完全在主线程内发生的交互也是如此。就好像该线程是唯一的线程一样。

唯一有趣的地方是当您提交给执行器的任务与主线程完成的事情之间存在交互时。 (即,当两个线程访问相同的共享变量时。)在这种情况下,它就像任何其他具有两个线程的程序一样,因为它只是一个具有两个线程的程序。访问共享变量时,请务必使用

synchronized

块或 ReentrantLock,这样可能会没问题。


© www.soinside.com 2019 - 2024. All rights reserved.