关于Executors.newSingleThreadExecutor()的问题
这是关于以下代码的程序流程的问题:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Test {
public static void main(String args[]) {
ExecutorService service = null;
try {
service = Executors.newSingleThreadExecutor();
System.out.println("begin");
service.execute(() -> System.out.println("Printing zoo inventory"));
service.execute(() -> {
for(int i = 0; i< 3; i++)
System.out.println("Printing record: " + i);
});
service.execute(() -> System.out.println("printing zoo inventory"));
} finally {
if(service != null)
service.shutdown();
}
}
}
在上面的代码中,一旦我们越过“System.out.println(“begin”)...线程执行器会一个接一个地执行以下操作任务(基本上是一个 Runnable lambda)。
我的理解是,考虑到线程执行器“服务”执行,这些“任务”(sop(“printing...”)、for-loop、sop(“printing...”))将一个接一个地运行线程执行器上的每个 Runnable lambda。
除非 Runnable lambda 在当前行完成,否则程序流不会移动到下一行,我是否正确?例如,除非 Runnable lambda 为 sop("printing zooinventory") 完成......它不会在下一行启动下一个 Runnable lambda?
如果当前线程执行器使用计算密集型的 Runnable lambda,会发生什么情况?在这种情况下,下一行(如果它包含另一个 Runnable lambda)。是否必须等到当前线程执行程序完成任务?
回答
Q & A
-“除非无法运行的 lambda 在当前行完成,否则程序流不会移动到下一行是否正确?例如,除非 Runnable lambda 完成 sop("printing zoo inventory")...它不会启动下一行是下一个可运行的 lambda 吗?”
- 是的,你是对的。*
-"如果当前线程执行器行具有计算密集型的 Runnable lambda,会发生什么情况?在这种情况下,下一行(如果它包含另一个 Runnable lambda)...必须等到当前线程执行器完成任务? ”
- 是的,它必须等待。*
- “你最喜欢的颜色? ”
-红色。
( * ) 假设您正在检查 中的行为ExecutorService,将主线程(及其调用以执行某些操作)排除在等式之外,并专注于池化工作线程。主线程由它自己运行,如果他们决定,一些实现也可能会向它分配任务,这就是为什么这个注释。
Test & Compare
我将尝试将此执行器与多线程执行器进行比较,以显示两种方法之间的差异。
关于您的问题,主要针对阻塞等待场景,根据所使用的执行程序服务,给出的答案完全不同。使用第二个选项,答案将是:
-
如果工作人员可用,它将能够移动到下一行,即使当前行没有完成。
-
不,如果线程可用,则不必等待。
-
红色的。
SingleThreadExecutor
创建一个Executor,它使用单个工作线程在无界队列中运行。
任务保证按顺序执行,并且在任何给定时间不会有超过一个任务处于活动状态。
池中只有一个线程,因此所有任务都分配给它。如文档中所述,它将必须按顺序执行所有这些。做一个简单的测试,例如:
service = Executors.newSingleThreadExecutor();
service.execute(() -> System.out.println("Printing zoo inventory"));
service.execute(() -> {
try {Thread.sleep(5000); System.out.println("Woke up"); }
catch (InterruptedException e) {e.printStackTrace();}
});
service.execute(() -> System.out.println("Finish"));
输出
Printing zoo inventory // ---- [Thread 1]
//...5s
Woke up // ---- [Thread 1]
Finish // ---- [Thread 1] -{x}-
如这个至少平庸的时间图所示:
{task1} {task2} {task3}
^ ^ ^
| | (~5s) |
[Thread1]-->[Thread1]---------------------->[Thread1]->{x}
调试它,唯一可用的线程确认它是执行前两个任务的线程。图像示例来自原始 OP 的问题:
第三个任务的断点 - 已经完成了两个
MultiThreaded Pool
以FixedThreadPool为例。当多个工作线程可用时,进程的行为会发生变化;对于本示例,设置了两个线程。
与往常一样,应该仔细阅读文档:
创建一个线程池,该线程池重用固定数量的线程在共享的无界队列上运行。在任何时候,最多 nThreads 个线程将是活动处理任务。如果在所有线程都处于活动状态时提交了额外的任务,它们将在队列中等待,直到有线程可用
稍微修改了测试,添加一些关于工作线程的信息并执行一些额外的任务。
volatile boolean wifeAlarm = false;
//...
service = Executors.newFixedThreadPool(2);
设置后,执行多个任务:
service.execute(() -> System.out.println("Woke up fast -"
+ Thread.currentThread().getName()));
service.execute(() ->
{
try {
Thread.sleep(5000);
System.out.println("Woke up lazy - John where are you?? - {"+
Thread.currentThread().getName()+"}");
} catch (InterruptedException e){}
finally { wifeAlarm=true;}
});
service.execute(() -> System.out.println("Cleaning - {"
+ Thread.currentThread().getName()+"}"));
service.execute(() -> System.out.println("Making breakfast - {"
+Thread.currentThread().getName()+"}"));
service.execute(() -> System.out.println("Flirt with neighbour - {"
+Thread.currentThread().getName()+"}"));
service.execute(() -> System.out.println("Got her number - {"
+Thread.currentThread().getName()+"}"));
service.execute(() -> System.out.println("Send hot af pic - {"
+Thread.currentThread().getName()+"}"));
service.execute(() -> System.out.println("Remove all proof on phone - {"
+Thread.currentThread().getName()+"}"));
service.execute(() ->
{
try {
while (!wifeAlarm)
Thread.sleep(13);
System.out.println("Just working my love - {"+
Thread.currentThread().getName()+"}");
} catch (InterruptedException e) {}
});
输出
Woke up fast - {pool-1-thread-1}
Cleaning - {pool-1-thread-1}
Making breakfast - {pool-1-thread-1}
Flirt with neighbour - {pool-1-thread-1}
Got her number - {pool-1-thread-1}
Send hot af pic - {pool-1-thread-1}
Remove all proof on phone - {pool-1-thread-1}
// ~4-5s
Woke up lazy - John where are you?? - {pool-1-thread-2}
Just working my love - {pool-1-thread-1}
这只是另一个可怕的表示:
{task1} {task2} {task3} (..) {task9}
^ ^ ^ ^ (~5s)
| [Thread2]--- | -------------|---------(...)----------->{x}
[Thread1] ----->[Thread1]--(..)-[Thread1]----------------------->{x}
在简历中:John 已经利用了这个新的上下文,通过多线程池实现了一些伟大的成就。
John 能够执行 7 个操作,而线程 2 执行 1 个。对 John 来说更好的是,他能够在线程 2 完成分配的任务之前完成所有这些操作。John 现在是安全的,它将完成它的任务并进入 IDLE,因为队列是空的。对约翰有好处。
thread-2被分配任务2。但是这次睡眠不会导致工作队列的增加,因为另一个线程能够并发执行它们。thread-1执行所有4 个重要任务:5, 6, 7 和 8。它还分配了其他 4 个低优先级任务,能够在另一个线程“忙碌”(睡眠)时清空工作队列。