首页 > 解决方案 > 在请求之间共享应用程序线程池

问题描述

我有一个基于 Spring 的应用程序,它带有一个 REST 服务,它导入一个 CSV 文件,解析它的内容,并在一些记录处理之后将数据(再次记录)存储到数据库中。将数据存储到数据库非常耗时,因为文件可以增长到数千条记录。

现在我想到了多线程 - 将处理和存储到数据库的数据委托给线程。我认为这是个好主意,但后来我想通了:等待 - 可能有多个用户同时导入文件,因此创建每个请求具有数百个线程池的线程池可能不是一个好主意

是否可以为每个应用程序创建一个包含 1000 个线程的线程池,并在到达应用程序的所有请求之间共享这个线程池 - 是的。但是我如何限制每个线程占用的线程数?

我想限制每个应用程序的线程数以不使服务器超载,并且我想限制每个请求所占用的线程数,以使没有一个线程消耗所有可用资源(特别是在这种情况下的线程)并饿死所有其他资源可能会出现的请求...

有什么想法、想法吗?

标签: multithreadingspring-bootsharethreadpool

解决方案


重新表述你的问题

这是一个相当复杂的情况。我将尝试改写它,看看我是否正确理解了您想要的行为。

您有几个应用程序可能会接收要在中央数据库上进行的批量更新。这些更新分批来自 CSV 文件,每批可以包含数千条记录。您希望并行处理这些更新,但是:

  1. 您想限制每个应用程序使用的线程数
  2. 如果并行处理多个批次,您希望保证单个批次的进度

第一个有缺陷的提案

要限制每个应用程序使用的线程数,您可以为每个应用程序使用固定大小的执行器服务。通过为每个 ExecutorService 的底层线程池赋予适当的大小,可以保证单个应用程序不会饿死其他应用程序。

然后可以将单个记录作为单个任务提交给执行器服务。如果单个应用程序正在处理多个批次,则来自这些批次的各个记录将被放入 Executor 服务中的单个队列中。来自不同批次的记录将交错,因为它们混合在执行器服务的单个队列中。

这个解决方案的问题是它不能保证所有批次都同时处理。假设您有一个包含 4 个线程的池来支持执行程序服务。如果有大批量的记录提交给执行器服务,所有 4 个线程都会开始处理这些记录。现在,如果第二批进来,它将在第一批之后加入队列,这意味着 4 个线程将在处理第二批之前处理第一批的所有记录。这很好,因为 4 个线程一直保持忙碌,但这不是您想要的行为。在这种情况下,您希望池中至少有一个线程开始处理第二批的记录,对吗?

一个可能的解决方案?

我认为您可以实现类似于适合您特定问题的 Fixed Thread Executor 服务的东西。这就是我要做的。

您可以创建一个线程池(以下称为“工作线程”)来处理来自多个队列的单个记录。一个队列对应一批记录。当需要处理一个新批次时,创建一个新队列并将其插入队列环中,工作线程从中获取记录以进行处理。将您的记录放入其中,以便线程可以在另一端处理它们,并在您的批处理完成时从环中删除队列(队列为空,您没有更多记录可放入此批处理)。所有队列都保持在一个环中,以便每个线程都可以遵循以下例程:

  1. 处理队列中的记录
  2. 移动到下一个队列(比如说右边的队列)
  3. 从第 1 步开始重复

使用这样的方案,您可以确保无论有多少批次来来去去,它们都会取得进展,即使批次多于需要处理它们的线程。如果当前只有一个正在处理,工作线程也将能够专注于单个批次。

我建议您使用ConcurrentLinkedQueue 之类的东西来管理您的批次。显然,在实现这种机制时存在许多编程陷阱。

  • 当队列从环中移除时,工作线程对队列的并发访问,
  • 当没有队列并且没有要处理的记录时如何处理工作线程),
  • 当工作人员检查队列中的记录但没有要处理的记录时你会怎么做(跳到下一个队列?)
  • 当只有一个队列但所有记录都已处理完毕并且工作人员正在挨饿时(阻塞地等待队列等待记录到来?如果批处理实际上完成了怎么办)

鉴于您的问题的复杂性,您似乎很有经验,我认为您应该能够弄清楚这一点。如果不是,我希望在 StackOverflow 上看到您提出的更多问题!


推荐阅读