关于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 个低优先级任务,能够在另一个线程“忙碌”(睡眠)时清空工作队列。

以上是关于Executors.newSingleThreadExecutor()的问题的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>