我有一个基于REST的基于Spring的应用程序,该服务导入CSV文件,解析其内容,并在逐条记录处理后将数据(再次逐条记录)存储到数据库中。将数据存储到数据库非常耗时,因为文件可能会增长到数千条记录。
现在,我想到了多线程-将处理和数据存储委托给线程。我想是好主意,但后来我想出了:等待-可能有多个用户同时导入文件,因此创建每个请求包含数百个主题的线程池可能不是一个好方法想法。
是否可以为每个应用程序创建一个例如1000个线程的线程池,并在到达该应用程序的所有请求之间共享该线程池-是的。但是如何限制每个线程占用的线程数?
我想限制每个应用程序的线程数以不使服务器过载,并且我想限制每个请求占用的线程数以使一个线程不消耗所有可用资源(特别是那种情况下的线程),并且饿死了所有其他可能出现的请求...
有什么想法,想法吗?
答案 0 :(得分:1)
我们可以使用信号量来限制访问特定资源的并发线程数。java.util.concurrent.Semaphore
在下面的示例中,我们将实现一个简单的登录队列以限制系统中的用户数:
class LoginQueueUsingSemaphore {
private Semaphore semaphore;
public LoginQueueUsingSemaphore(int slotLimit) {
semaphore = new Semaphore(slotLimit);
}
boolean tryLogin() {
return semaphore.tryAcquire();
}
void logout() {
semaphore.release();
}
int availableSlots() {
return semaphore.availablePermits();
}
}
请注意我们如何使用以下方法:
tryAcquire()
如果立即可以获得许可证,则返回true,否则获取false;但是,acquire()获取许可证并阻塞直到一个可用。
release()
发放许可证
availablePermits()
返回当前可获得的许可证数量
PS:示例使用来自site
答案 1 :(得分:1)
重述您的问题
这是一个非常复杂的情况。我将尝试对其重新措辞,以了解我是否正确理解了您期望的行为。
您有几个应用程序,它们可能会接收要在中央数据库上进行的批量更新。这些更新来自CSV文件,每批可以包含数千条记录。您想并行处理这些更新,但是:
第一个有缺陷的建议
要限制每个应用程序使用的线程数,可以为每个应用程序使用fixed-size executor service之类的东西。通过为每个ExecutorService的基础线程池提供适当的大小,可以保证单个应用程序不会饿死其他应用程序。
然后,单个记录可以作为单个任务提交给执行者服务。如果单个应用程序正在处理多个批处理,则来自这些批处理的各个记录将被放入Executor服务的单个队列中。来自不同批次的记录将在执行器服务的单个队列中混合时交织。
此解决方案的问题在于,它不能保证所有批次都可以同时处理。假设您有4个线程池来支持执行程序服务。如果将大量记录提交给执行者服务,则所有4个线程将开始处理这些记录。现在,如果有第二批,它将被添加到第一批之后的队列中,这意味着这四个线程将在处理第二批之前处理第一批的所有记录。很好,因为4个线程始终保持忙碌,但这不是您想要的行为。在这种情况下,您希望池中的至少一个线程开始处理第二批记录,对吗?
可能的解决方案?
我认为您可以实现与固定线程执行器服务类似的功能,以解决您的特定问题。这就是我要做的。
您可以创建线程池(以下称为“工作人员线程”)来处理来自多个队列的单个记录。队列对应于一批记录。当需要处理新批处理时,请创建一个新队列并将其插入队列中,工作线程正在从该队列中获取要处理的记录。将记录扔进去,以便线程可以在另一端处理它们,并在批处理完成时将队列从环中删除(队列为空,没有更多记录可放入此批处理中)。所有队列都保持环形,以便每个线程可以遵循以下例程:
使用这样的方案,您可以确保不管有多少批次进出,它们都将取得进展,即使批次多于需要照顾的线程。如果当前仅处理一个工作线程,则工作线程也将能够专注于单个批处理。
我建议您使用ConcurrentLinkedQueue之类的东西来管理批次。显然,实现这种机制存在许多编程陷阱。
鉴于问题的复杂性,您似乎很有经验,我认为您应该能够弄清楚这一点。如果不是这样,我希望在StackOverflow上看到您的更多问题!