Java—如何在等待数据的进程上实现线程
我对多线程编程相当陌生,我希望能够深入了解实现以下想法的最佳方法 现在我的代码是这样工作的 它是单线程的,因此在处理每一段数据并将其写入数据库所需的时间内,新数据进入并排队,这会大大降低速度。我在4CPU服务器上运行,但当前设置仅使用1 我想把剩下的3个CPU的中间部分的工作分开。我最好怎么做?我以为我可以为每一条新数据创建一个新线程,但我们讨论的是一天中数十万个新线程。据我所知,与此相关的开销将非常大。记忆对我来说是一个问题,所以如果所有这些线程的创建消耗了太多的记忆,我将陷入困境。一个新线程会使用一个不那么繁忙的CPU,还是因为它是同一个JVM而使用同一个CPU 如果需要的话,每个新数据段的处理和DB写入不应超过几秒钟 我也读过关于线程池的文章,但是这个想法让我有点困惑,我找不到一个很好的例子 我是这么想的Java—如何在等待数据的进程上实现线程,java,multithreading,threadpool,Java,Multithreading,Threadpool,我对多线程编程相当陌生,我希望能够深入了解实现以下想法的最佳方法 现在我的代码是这样工作的 它是单线程的,因此在处理每一段数据并将其写入数据库所需的时间内,新数据进入并排队,这会大大降低速度。我在4CPU服务器上运行,但当前设置仅使用1 我想把剩下的3个CPU的中间部分的工作分开。我最好怎么做?我以为我可以为每一条新数据创建一个新线程,但我们讨论的是一天中数十万个新线程。据我所知,与此相关的开销将非常大。记忆对我来说是一个问题,所以如果所有这些线程的创建消耗了太多的记忆,我将陷入困境。一个新线
请帮助多线程新手想出一个合理的设计!提前感谢:-(<>P>)首先,你应该考虑在这个应用程序中有多重要的性能以及需要处理什么样的流量。如果您不太关心为每个请求增加0.1毫秒的延迟(如果您说每个请求需要几秒钟,我想您不会这么做),那么创建一个新线程将不会是一个明显的成本。请注意,您的线程应该在完成工作后结束其生命周期,这样您就不会同时拥有数十万个线程——它们将随着时间的推移而开始和结束。如果您每天收到“几十万”个请求,那么这只是每秒几个请求(假设它们被平均分配)。使用这些参数,您的活动请求的平均数量大约为几十个(大约每秒10个,每次请求几秒钟~=任何时候都有几十个请求)。这比您机器上的内核数量还要多,但处理时应该没有任何问题-如果这些线程与DB通信,它们将花费大部分时间等待通信链路。虽然每个请求都有一个单独的线程通常不是最好的设计,但它可能比学习未来和执行者之类的花哨东西更容易实现
因此,这两种解决方案都有各自的优点——未来会有更好的设计,可能会有更好的资源利用率(尽管这可能取决于您对它们的调度情况)以及每个请求的线程数,以快速完成任务(并且能够轻松理解系统内部的情况)。如果您现在只学习并发性,我实际上会建议您首先使用不那么优雅的方式来理解系统在幕后需要做什么。然后,当您熟悉“手动”调度方法时,您可以进入更高的抽象级别,学习Futures等,并重构代码。第二个版本可能比立即开始使用Futures编写的代码要好得多。更重要的一点是,有多少线程并行工作(从而可能导致一台机器死机)。如果您一个接一个地创建线程对象,那么可以更高效地完成,但一般来说,这样做的成本(可能)可以忽略不计(正如Michal指出的)。撇开这一点不谈(假设您想学习多线程),您的设计听起来已经足够合理了。现在,我们来看一看,在实现这一目标的工具方面,什么可以为您提供尊重:
:将是最佳选择。创建一个包含ExecutorService
工作线程的固定线程池,然后针对每个传入线程,发布一个n
执行处理并将所有数据存储到数据库中Runnable
public class DataProcessor { final ExecutorService workerThreadPool = Executors.newFixedThreadPool(5); public void onNewDataFromTheOutsideWorld(Data d) { workerThreadPool.execute(new ProcessingAndStoreToDBRunnable(d)); } public void onShutdown() { workerThreadPool.shutdown(); } }
将确保只有固定数量的工作人员实际并行运行ExecutorService
- 自己的排队机制:当涉及到具有不同优先级的作业时,您可能希望实现自己的工作机制。请注意,这要复杂得多,如果可能的话,您可能应该坚持使用
解决方案 基本思想是拥有一个ExecutorService
,向其中添加数据,并启动从队列读取作业的阻塞队列
工作线程。诀窍是,如果没有作业,队列将阻塞(从而使线程进入睡眠状态),如果作业超过n
个,则作业将存储在队列中,直到处理线程可用为止n
public class DataProcessor { final BlockingQueue<Data> queue = new BlockingQueue<Data>(); public void onInit() { for (int i = 0; i < n; i++) new Thread(new WorkerRunnable(queue)).start(); } public void onNewDataFromTheOutsideWorld(Data d) { queue.add(d); } } public class WorkerRunnable implements Runnable { public void run() { while (true) { Data d = queue.take(); processData(d); } } }
正如我所说的,这要复杂得多,因为我还没有触及像这样的问题公共类数据处理器{ 最终阻塞队列=新阻塞队列(); 公共无效onInit(){ 对于(int i=0;i
- 停止工作线程
- 确保处理异常,然后返回处理
- 等等