Java - 如何在等待数据的进程上实现线程

时间:2012-12-11 20:54:32

标签: java multithreading threadpool

我对多线程编程很新,我希望能够深入了解实现以下想法的最佳方法。

现在我的代码就是这样的 Current functionality

它是单一的线程,所以在处理每个数据并将其写入数据库所需的时间内,新数据进入并排队等待,这会使事情变得过于缓慢。我在4CPU服务器上运行,但当前设置仅使用1.

我想将剩下的3个CPU中间件完成的工作分开。我最好怎么做?我认为我可以为每个新数据创建一个新线程,但我们正在讨论一天中成千上万的新线程。从我读到的,与此相关的开销将非常大。内存对我来说是一个问题因此,如果所有这些线程的创建都占用太多内存,我将遇到麻烦。新线程是否会使用不太繁忙的CPU,或者它是否使用相同的CPU,因为它是相同的JVM?

每个新数据的处理和DB写入不应超过几秒钟,如果这样的话。

我还在阅读有关线程池的内容,但这个想法对我来说有点混乱,我找不到一个很好的例子。

我在想这样的事情 Idea

请提出合理的设计,帮助多线程新手!在此先感谢: - )

2 个答案:

答案 0 :(得分:6)

更重要的一点是,有多少线程并行工作(因此可能会杀死一台机器)。如果你一个接一个地创建一个Thread对象,它可以更有效地完成,但一般来说,它的成本(可能)可以忽略不计(正如Michal指出的那样)。抛开这一点(假设您想了解多线程),您的设计已经足够合理了。现在去看看java.util.concurrent可以为您提供哪些工具来实现它:

  • 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解决方案。

    基本思想是添加一个BlockingQueue数据并启动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);
           }
        }
     }
    

    正如我所说,实现起来要复杂得多,因为我还没有触及像

    这样的问题
    • 停止工作线程
    • 确保处理异常,然后返回处理
    • 等。

这些只是多线程环境中的基本(但非常强大)工具。如果您需要更多高级工具,请查看Guava库,例如使用ListenableFuture的精彩概念(如果您需要工作线程的结果,则应该使用它。)

然后您将拥有一个相当基本的设计,您可以在其中添加一些更复杂的处理步骤,如您的评论中已经指出的那样。还有人指出,它然后提出了一个相当广泛的问题;)

答案 1 :(得分:0)

首先,您应该考虑此应用程序中性能的重要性以及您需要处理的流量类型。如果你不太关心为每个请求添加0.1毫秒的延迟(我认为如果你说每个请求需要几秒钟就不会这样),那么创建一个新线程将不会是一个明显的代价。请注意,您的线程应该在完成工作后结束生命,因此您不会同时拥有数十万个线程 - 它们将随着时间的推移而开始和结束。如果每天收到“几十万”请求,这只是每秒几个请求(假设它们被均匀分割)。使用这些参数,您的平均活动请求数将大约为几十个(每个请求大约10次/秒,几秒钟〜=几十个请求随时都有)。这不仅仅是您机器上的核心数量,而且应该没有任何问题进行处理 - 如果这些线程与数据库通信,他们将花费大部分时间等待通信链接。虽然为每个请求设置一个单独的线程可能不是一般的最佳设计,但实现起来可能比学习Futures和Executors更加容易。

所以这两种解决方案都有它们的优点 - 期望更好的设计和可能更好的资源使用(尽管这可能取决于你如何安排它们)和每个请求的线程以使某些东西快速工作(并且能够轻松地理解发生了什么在系统内部)。如果你现在只学习并发,我实际上建议你先用不那么优雅的方式来理解系统在幕后需要做什么。然后,当您熟悉这种“手动”调度方法时,您可以进入更高级别的抽象,学习Futures等,并重构您的代码。如果你马上开始使用Futures,第二个版本可能会比你能编写的代码好得多。