等待线程资源消耗

时间:2016-07-07 00:00:45

标签: java multithreading performance timer

我的问题:

当线程为TIMED_WAIT状态(未休眠)> 99.9%的时间时,JVM中的大量线程是否会消耗大量资源(内存,CPU)?线程在等待时,如果需要维护它们需要花费多少CPU开销呢?

答案是否也适用于非JVM相关环境(如Linux内核)?

上下文

我的程序收到大量空间消耗包。它在不同的包中存储类似属性的计数。在收到包裹(可能是几小时或几天)后的一段时间后,该特定包裹到期并且包裹所贡献的任何计数应该减少。

目前,我通过将所有软件包存储在内存或磁盘中来实现这些功能。每隔5分钟,我从存储中删除过期的包,并扫描剩余的包以计算属性。此方法占用大量内存,并且时间复杂度较低(O(n)表示时间和内存,其中n是未到期包的数量)。这使程序的可扩展性变得糟糕。

解决此问题的另一种方法是每次打包时增加属性计数,并启动一个Timer()线程,该线程在包过期后递减属性计数。这样就无需存储所有庞大的包,并将时间复杂度降低到O(1)。但是,这会产生另一个问题,因为我的程序将开始具有O(n)个线程,这可能会降低性能。由于大多数线程在其生命周期的绝大部分时间内处于TIMED_WAIT状态(Java的Timer()调用Object.wait(long)方法),它是否仍会以非常大的方式影响CPU?

1 个答案:

答案 0 :(得分:20)

首先,Java(或.NET)线程!=内核/ OS线程。

Java Thread是一个高级包装器,它抽象出系统线程的一些功能;这些类型的线程也称为托管线程。在内核级别,线程只有2个状态,运行且未运行。有一些内核跟踪的管理信息(堆栈,指令指针,线程ID等),但内核级别没有这样的事情,因为处于TIMED_WAITING状态的线程(.NET等同于WaitSleepJoin状态)。那些"州"仅存在于这些类型的上下文中(为什么C ++ std::thread没有state成员)。

话虽如此,当托管线程被阻止时,它会以两种方式完成(取决于如何在托管级别阻止它被阻止);我在OpenJDK中看到的用于线程代码的实现利用信号量来处理托管等待(这是我在其他C ++框架中看到的那种具有某种"托管"线程的东西类和.NET Core库中的类,并将互斥锁用于其他类型的等待/锁定。

由于大多数实现都会使用某种锁定机制(如信号量或互斥量),因此内核通常会做同样的事情(至少在你的问题所在的地方);也就是说,内核将把线程从"运行"排队并把它放在"等待"队列(a context switch)。进入线程调度,特别是内核如何处理线程的执行超出了本Q& A的范围,特别是因为您对Java有疑问,Java可以运行在很多不同类型的操作系统(每个操作系统完全不同地处理线程)。

更直接地回答您的问题:

  

当线程处于TIMED_WAIT状态(非休眠状态)> 99.9%的时间时,JVM中的大量线程是否会消耗大量资源(内存,CPU)?

对此,有几点需要注意:创建的线程消耗JVM(堆栈,ID,垃圾收集器等)的内存,内核使用内核内存来管理内核级别的线程。除非您特别说明,否则消耗的内存不会改变。因此,如果线程处于休眠或运行状态,则内存是相同的。

CPU将根据线程活动和请求的线程数进行更改(请记住,线程也会消耗内核资源,因此必须在内核级别进行管理,因此需要处理的线程越多,必须消耗更多的内核时间来管理它们。)

请记住,计划和运行线程的内核时间非常短(这是设计要点的一部分),但是如果你计划运行一个内容,它仍然需要考虑很多的主题;此外,如果您知道您的应用程序将在仅具有少数核心的CPU(或群集)上运行,则您可用的核心越少,内核切换到上下文的时间就越多,通常会增加额外的时间。

  

当线程在等待时,如果需要任何线程,维护它们需要多少CPU开销?

无。如上所述,但用于管理线程的CPU开销不会根据线程上下文而改变。额外的CPU可能用于上下文切换,并且当活动时线程本身将使用额外的CPU,但是没有额外的"成本"到CPU 维护等待线程与正在运行的线程。

  

答案是否也适用于非JVM相关环境(如Linux内核)?

是和否。如上所述,托管上下文通常适用于大多数类型的环境(例如Java,.NET,PHP,Lua等),但这些上下文可以变化,并且线程习语和一般功能取决于所使用的内核。因此,虽然一个特定内核可能能够处理每个进程1000多个线程,但有些内核可能有硬限制,其他内核可能存在其他问题,每个进程的线程数更多;您必须参考操作系统/ CPU规格,看看您可能有哪些限制。

  

由于大多数线程都处于TIMED_WAIT状态(Java的Timer()调用Object.wait(long)方法)绝大部分生命周期,它是否仍会以非常大的方式影响CPU?

否(被阻止线程的一部分),但需要考虑的事项:如果(边缘情况)这些线程的所有(或> 50%)需要在同一时间运行,该怎么办?如果您只有几个线程来管理您的软件包,那可能不是问题,但是说您有500多个;所有同时被唤醒的250个线程都会导致大量的CPU争用。

由于您尚未发布任何代码,因此很难对您的方案提出具体建议,但有人会倾向于将属性结构存储为类,并将该类保留在列表中或可以在Timer(或单独的线程)中引用的哈希映射,以查看当前时间是否与包的到期时间匹配,然后"到期"代码会运行。这将线程数减少到1,访问时间减少到O(1);但是,如果没有代码,该建议可能在您的方案中不起作用。

希望有所帮助。