代码之家  ›  专栏  ›  技术社区  ›  Arne Claassen

Java线程池与高请求场景中的新线程

  •  8
  • Arne Claassen  · 技术社区  · 17 年前

    我有一些REST服务的旧java代码,该服务为每个传入请求使用单独的线程。也就是说,主循环将在socket.accept()上循环,并将套接字交给Runnable,然后Runnable将启动自己的后台线程并调用run。这在一段时间内运行得很好,直到最近我才注意到,在高负载下,接受处理请求的延迟将变得不可接受。当我赞赏地说“好”时,我的意思是它每秒处理100-200个请求,而CPU使用率不高。只有当其他守护进程也在增加负载时,性能才会下降,而且只有一次负载超过5。当机器处于其他进程组合的高负载(5-8)下时,从接受到处理的时间会变得高得离谱(500ms到3000ms),而实际处理时间则保持在10ms以下。这一切都是在双核centos 5系统上进行的。

    已经习惯了Threadpools。NET,我认为线程创建是罪魁祸首,我想我会在java中应用同样的模式。现在我的Runnable是用ThreadPool执行的。执行器(池使用和ArrayBlockingQueue)。同样,它在大多数情况下都很有效,除非机器负载很高,那么从创建可运行文件到调用run()的时间也差不多。但更糟糕的是,在线程池逻辑就绪的情况下,系统负载几乎翻了一番(10-16)。所以现在我遇到了同样的延迟问题,负载加倍。

    谢谢,

    Executors.newFixedThreadPool(100);

    4 回复  |  直到 17 年前
        1
  •  12
  •   Stephen Denne    17 年前

    配置如下:

    new ThreadPoolExecutor(10, 100, 30, TimeUnit.SECONDS, 
            new ArrayBlockingQueue<Runnable>(100))
    

    然后,一旦10个线程同时处理请求,就会向队列中添加更多请求,除非队列中的请求达到100个,此时它将开始创建新线程,除非已经有100个线程,否则命令的处理将被拒绝。

    该部分 javadocs of ThreadPoolExecutor (复制到下面)可能值得一读。

    基于它们,以及你显然愿意运行100个线程,以及你接受所有请求并最终处理它们的愿望。。我建议尝试以下变体:

    new ThreadPoolExecutor(100, 100, 0, TimeUnit.SECONDS, 
            new LinkedBlockingQueue<Runnable>())
    

    顺便说一句,这就是你会得到的 Executors.newFixedThreadPool(100);


    排队

    任何BlockingQueue均可用于传输和保存已提交的任务。此队列的使用与池大小相互作用:

    • 如果运行的线程数少于corePoolSize,则执行器总是更喜欢添加新线程而不是排队。
    • 如果corePoolSize或更多线程正在运行,执行器总是更喜欢排队请求,而不是添加新线程。
    • 如果请求无法排队,则会创建一个新线程,除非这会超过maximumPoolSize,在这种情况下,任务将被拒绝。

    排队有三种一般策略:

    1. 直接交接。工作队列的一个很好的默认选择是SynchronousQueue,它将任务交给线程,而不会以其他方式持有它们。在这里,如果没有立即可用的线程来运行任务,则尝试对任务进行排队将失败,因此将构造一个新线程。此策略在处理可能具有内部依赖关系的请求集时避免了锁定。直接切换通常需要无限的最大池大小,以避免拒绝新提交的任务。这反过来又承认,当命令的平均到达速度超过其处理速度时,线程可能会无限增长。
    2. 无限的队列。使用无界队列(例如没有预定义容量的LinkedBlockingQueue)将导致新任务在所有corePoolSize线程都忙时在队列中等待。因此,创建的线程数量不会超过corePoolSize。(因此,maximumPoolSize的值没有任何影响。)当每个任务完全独立于其他任务时,这可能是合适的,这样任务就不会影响彼此的执行;例如在网页服务器中。虽然这种排队方式在平滑短暂的请求爆发方面很有用,但它承认,当命令平均到达速度超过处理速度时,工作队列可能会无限增长。
        2
  •  4
  •   Charlie Martin    17 年前

    测量,测量,测量!它把时间花在哪里了?当你创建Runnable时,会发生什么?Runnable在实例化过程中是否有任何可能阻塞或延迟的东西?在延误期间发生了什么?

    运行时环境、JVM版本和体系结构是什么?

        3
  •  1
  •   Tom Hawtin - tackline    17 年前

    孙的实施 Thread 虽然比以前快得多,但确实有锁定功能。IIRC, ArrayBlockingQueue 忙碌时根本不应该锁定。因此,现在是分析器时间(甚至只是几个时间) ctrl-\ s或 jstack

    系统负载只是告诉有多少线程排队。它不一定很有用。

        4
  •  0
  •   TofuBeer    17 年前

    一、 仅仅 这是用我自己的代码做的。我使用Netbeans分析器来转换我正在使用的线程池实现。你应该能够用它做同样的事情 Visual VM 但我还没有试过。