简体   繁体   English

单个 ScheduledExecutorService 实例与多个 ScheduledExecutorService 实例

[英]Single ScheduledExecutorService instance vs Multiple ScheduledExecutorService instances

I have a service which schedules async tasks using ScheduledExecutorService for the user.我有一个使用 ScheduledExecutorService 为用户安排异步任务的服务。 Each user will trigger the service to schedule two tasks.每个用户都会触发服务来安排两个任务。 (The 1st Task schedule the 2nd task with a fixed delay, such as 10 seconds interval) pseudocode code illustration: (1st Task以固定的延迟时间安排2nd task,比如10秒间隔)伪代码代码说明:

task1Future = threadPoolTaskScheduler.schedule(task1);
for(int i = 0; i< 10000; ++i) {
    task2Future = threadPoolTaskScheduler.schedule(task2);
    task2Future.get(); // Takes long time
    Thread.sleep(10);
}

task1.Future.get();

Suppose I have a potential of 10000 users using the service at the same time, we can have two kinds of ScheduledExecutorService configuration for my service:假设我有可能有 10000 个用户同时使用该服务,我们可以为我的服务配置两种 ScheduledExecutorService:

  1. A single ScheduledExecutorService for all the users.所有用户的单个 ScheduledExecutorService。
  2. Create a ScheduledExecutorService for each user.为每个用户创建一个 ScheduledExecutorService。

What I can think about the first method:我能想到的第一种方法:

Pros:优点:

  1. Easy to control the number of threads in the thread pool.易于控制线程池中的线程数。
  2. Avoid creating new threads for scheduled tasks.避免为计划任务创建新线程。

Cons:缺点:

  1. Always keeping multiple number of threads available could waste computer resources.始终保持多个线程可用可能会浪费计算机资源。
  2. May cause the hang of the service because of lacking available threads.可能由于缺少可用线程而导致服务挂起。 (For example, set the thread pool size to 10, and then there is a 100 person using the service the same time, then after entering the 1st task and it tries to schedule the 2nd task, then finding out there is no thread available for scheduling the 2nd task) (比如设置线程池大小为10,然后有100个人同时使用服务,然后进入第1个任务后尝试调度第2个任务,发现没有可用的线程安排第二个任务)

What I can think about the second method我能想到的第二种方法

Pros:优点:

  1. Avoiding always keep many threads available when the number of user is small.避免在用户数量少的情况下总是保持很多线程可用。
  2. Can always provide threads for a large number of simultaneously usage.总是可以提供线程供大量同时使用。

Cons:缺点:

  1. Creating new threads creates overheads.创建新线程会产生开销。
  2. Don't know how to control the number of maximum threads for the service.不知道如何控制服务的最大线程数。 May cause the RAM out of space.可能导致 RAM 空间不足。

Any ideas about which way is better?关于哪种方式更好的任何想法?

Single ScheduledExecutorService drives many tasks单个ScheduledExecutorService驱动许多任务

The entire point of a ScheduledExecutorService is to maintain a collection of tasks to be executed after a certain amount of time elapses. ScheduledExecutorService的全部要点是维护一组在经过一定时间后要执行的任务。

So given the scenario you describe, you need only a single ScheduledExecutorService object. Submit your 10,000 tasks to that one object. Each task will be executed approximately when its designated delay elapses.因此,根据您描述的场景,您只需要一个ScheduledExecutorService object。将您的 10,000 个任务提交给该 object。每个任务将在其指定的延迟结束时大约执行。 Simple, and easy.简单,容易。

Thread pool size线程池大小

The real issue is deciding how many threads to assign to the ScheduledExecutorService .真正的问题是决定将多少线程分配给ScheduledExecutorService

Threads, as currently implemented in the OpenJDK project, are mapped directly to host OS threads.目前在OpenJDK项目中实现的线程直接映射到主机操作系统线程。 This makes them relatively heavyweight in terms of CPU and memory usage.这使得它们在 CPU 和 memory 使用方面相对重量级。 In other words, currently Java threads are “expensive”.换句话说,目前 Java 个线程是“昂贵的”。

There is no simple easy answer to calculating thread pool size.计算线程池大小没有简单的答案。 The optimal number is the least amount of threads that can keep up with the workload without over-burdening the host machine's limited number of cores and limited memory. If you search Stack Overflow, you'll find many discussions on the topic of deciding how many threads to use in a pool.最佳数量是可以跟上工作负载的最少线程数,而不会使主机的有限数量的内核和有限的 memory 负担过重。如果你搜索 Stack Overflow,你会发现很多关于决定多少线程的讨论在池中使用的线程。

Project Loom织机计划

And keep tabs with the progress of Project Loom and its promise to bring virtual threads to Java. That technology has the potential to radically alter the calculus of deciding thread pool size.并密切关注Project Loom及其 promise 的进展,将虚拟线程带到 Java。该技术有可能从根本上改变决定线程池大小的计算方法。 Virtual threads will be more efficient with CPU and with memory. In other words, virtual threads will be quite “cheap”, “inexpensive”.使用 CPU 和 memory 时,虚拟线程会更高效。换句话说,虚拟线程将非常“廉价”、“廉价”。

How executor service works执行者服务如何运作

You said:你说:

entering the 1st task and it tries to schedule the 2nd task, then finding out there is no thread available for scheduling the 2nd task输入第一个任务并尝试安排第二个任务,然后发现没有可用于安排第二个任务的线程

That is not how the scheduled executor service (SES) works.这不是预定执行程序服务 (SES) 的工作方式。

If a task being currently executed by a SES needs to schedule itself or some other task to later execution, that submitted task is added to the queue maintained internally by the SES.如果 SES 当前正在执行的任务需要安排自己或其他一些任务稍后执行,则该提交的任务将添加到 SES 内部维护的队列中。 There is no need to have a thread immediately available.不需要立即可用的线程。 Nothing happens immediately except that queue addition.除了添加队列外,什么都不会立即发生。 Later, when the added task's specified delay has elapsed, the SES looks for an available thread in its thread-pool to execute that task that was queued a while back in time.稍后,当添加的任务的指定延迟已经过去时,SES 会在其线程池中寻找可用线程来执行该任务,该任务已及时排队。

You seem to feel a need to manage the time of each task's execution on certain threads.您似乎觉得需要管理每个任务在特定线程上的执行时间。 But that is the job of the scheduled executor service.但这是预定执行程序服务的工作。 The SES tracks the tasks submitted for execution, notices when their specified delay elapses, and schedules their execution on a thread from its managed pool of threads. SES 跟踪提交执行的任务,通知它们指定的延迟时间,并安排它们在其管理的线程池中的线程上执行。 You don't need to manage any of that.您不需要管理任何这些。 Your only challenge is to assign an appropriate number of threads to the pool.您唯一的挑战是为池分配适当数量的线程。

Multiple executor services多个执行者服务

You commented:你评论说:

why don't use multiple ScheduledExecutorService instances为什么不使用多个 ScheduledExecutorService 实例

Because in your scenario, there is no benefit.因为在你的场景中,没有任何好处。 Your Question implies that you have many tasks all similar with none being prioritized.您的问题暗示您有许多任务都相似,但没有一个是优先的。 In such a case, just use one executor service.在这种情况下,只需使用一个执行程序服务即可。 One scheduled executor service with 12 threads will get the same amount of work accomplished as 3 services with 4 threads each.一个具有 12 个线程的计划执行程序服务将完成与 3 个服务(每个服务具有 4 个线程)相同的工作量。

As for excess threads, they are not a burden.至于多余的线程,它们不是负担。 Any thread without a task to execute uses virtually no CPU time.没有要执行的任务的任何线程几乎不使用 CPU 时间。 A pool may or may not choose to close some unused threads after a while.一段时间后,池可能会也可能不会选择关闭一些未使用的线程。 But such a policy is up to the implementation of the thread pool of the executor service, and is transparent to us as calling programmers.但是这样的策略取决于executor服务的线程池的实现,对于我们作为调用程序员来说是透明的。

If the scenario were different, where some of the tasks block for long periods of time, or where you need to prioritize certain tasks, then you may want to segregate those into a separate executor service.如果场景不同,某些任务会长时间阻塞,或者您需要确定某些任务的优先级,那么您可能希望将它们隔离到一个单独的执行程序服务中。

In today's Java (before Project Loom with virtual threads), when code in a thread blocks, that thread sits there doing nothing but waiting to unblock.在今天的 Java 中(在使用虚拟线程的 Loom 项目之前),当线程中的代码阻塞时,该线程坐在那里除了等待解除阻塞之外什么都不做。 Blocking means your code is performing an operation that awaits a response.阻塞意味着您的代码正在执行等待响应的操作。 For example, making.network calls to a socket or web service blocks, writing to storage blocks, and accessing an external database blocks.例如,对套接字或 web 服务块进行网络调用、写入存储块以及访问外部数据库块。 Ideally, you would not write code that blocks for long periods of time.理想情况下,您不会编写长时间阻塞的代码。 But sometimes you must.但有时你必须这样做。

In such a case where some tasks run long, or conversely you have some tasks that must be prioritized for fast execution, then yes, use multiple executor services.在这种情况下,有些任务运行时间很长,或者相反,您有一些任务必须按优先级排序才能快速执行,那么是的,使用多个执行程序服务。

For example, say you have a 16-core machine with not much else running except your Java app.例如,假设您有一台 16 核机器,除了您的 Java 应用程序外,没有其他任何东西在运行。 You might have one executor service with a thread pool size of 4 maximum for long-running tasks, one executor service with a thread pool with a size of 7 maximum for many run-of-the-mill tasks, and a third executor service with a thread pool maximum size of 2 for very few tasks that run short but must run quickly.对于长时间运行的任务,您可能有一个线程池大小最大为 4 的执行程序服务,对于许多普通任务,线程池大小最大为 7 的执行程序服务可能有一个,第三个执行程序服务具有对于运行时间短但必须快速运行的极少数任务,线程池最大大小为 2。 (The numbers here are arbitrary examples, not a recommendation.) (这里的数字是任意示例,而不是建议。)

Other approaches其他方法

As commented , there are other frameworks for managing concurrency.正如所评论的,还有其他用于管理并发性的框架。 The ScheduledExecutorService discussed here is general purpose.这里讨论的ScheduledExecutorService是通用的。

For example, Swing , JavaFX , Spring , and Jakarta EE each have their own concurrency management.例如, SwingJavaFXSpringJakarta EE都有自己的并发管理。 Consider using those where approriate to your particular project.考虑使用适合您的特定项目的那些。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM