2016-07-07 117 views
10

我的问题:等待线程资源消耗

是否大量存在于JVM线程消耗了大量的资源(内存,CPU),当线程TIMED_WAIT状态(不睡觉)>的99.9% ?当线程在等待时,如果有需要的话,维护它们需要多少CPU开销?

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

语境:

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

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

解决此问题的一种替代方法是在每次出现软件包时递增属性计数,并启动在软件包到期后递减属性计数的线程Timer()。这消除了存储所有大型软件包的需要,并将时间复杂度降低到O(1)。但是,这会产生另一个问题,因为我的程序将开始具有O(n)线程数,这可能会削减性能。由于大多数线程将处于TIMED_WAIT状态(Java的Timer()调用Object.wait(long)方法),它们的绝大部分生命周期都会以非常大的方式影响CPU吗?

+0

您认为有多少线程可能在等待?几百个左右的内核可能不会对核心检查线程以及他们需要安排的时间征税,但如果您排队等待超过500个,您可能需要重新考虑您的方法.. – txtechhelp

+0

我可能会有超过几百人。你能解释为什么内核必须经常检查'TIMED_WAIT'上的线程吗?我试图找到有关内核如何执行此操作的信息,但无法找到任何所需的信息。 – PhotometricStereo

+1

ScheduledExecutor /一个优先级队列通过单线程到期的时间戳。 – zapl

回答

9

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

一个Java Thread是一个高级包装器,它抽象了系统线程的一些功能;这些类型的线程也被称为托管线程。在内核级别,一个线程只有2个状态,运行而不运行。有一些内核跟踪的管理信息(堆栈,指令指针,线程ID等),但是在内核级别上没有这样的事情,因为线程处于TIMED_WAITING状态(.NET等价于WaitSleepJoin州)。这些“状态”只存在于这些类型的上下文中(为什么C++ std::thread没有state成员)的一部分。尽管如此,当一个托管线程被阻塞时,这是通过几种方式完成的(取决于它如何在托管级别被请求阻塞);我在OpenJDK中为线程代码看到的实现利用信号量来处理被管理的等待(这是我在其他C++框架中看到的,这些C++框架有一种“受管理”线程类以及.NET Core库),并为其他类型的等待/锁使用互斥锁。由于大多数实现都会使用某种锁定机制(如信号量或互斥锁),因此内核通常会做同样的事情(至少在您的问题涉及到的地方)。也就是说,内核会将线程从“运行”队列中取出并放入“等待”队列(一个context switch)。进入线程调度,特别是内核如何处理线程的执行超出了这个问题的范围,尤其是因为您的问题在于Java,Java可以在多种不同类型的OS上运行(每个处理完全不同的线程)。

更直接地回答你的问题:

是否大量线程的JVM消耗大量的资源(内存,CPU),当线程TIMED_WAIT状态(不睡觉)> 99.9%的时间内的?为此,有两点需要注意:创建的线程为JVM(堆栈,ID,垃圾收集器等)消耗内存,内核使用内核内存来管理内核中的线程水平。消耗的内存不会改变,除非你特别说明。所以如果线程正在休眠或运行,内存是相同的。

CPU将根据线程活动和请求的线程数量进行更改(请记住,线程也会消耗内核资源,因此必须在内核级别进行管理,因此必须处理的线程越多,管理它们必须消耗更多的内核时间)。请注意,计划和运行线程的内核时间非常短(这是设计要点的一部分),但如果您计划运行批次的线程,它仍然是需要考虑的事情;但是,如果您计划运行线程的批次,此外,如果您知道您的应用程序将在只有少量内核的CPU(或群集)上运行,则可供您使用的内核越少,内核越有必要进行上下文切换,从而增加额外的时间。

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

无。请参阅上文,但用于管理线程的CPU开销不会根据线程上下文而改变。额外的CPU可能用于上下文切换,当CPU处于活动状态时,线程本身会使用额外的CPU,但CPU对没有额外的“成本”,因此可以维持的等待线程与正在运行的线程。

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

是和否。如上所述,受管理的上下文通常适用于大多数这些类型的环境(例如,Java,.NET,PHP,Lua等),但是这些上下文可以变化,并且线程习惯用法和一般功能取决于正在使用的内核。因此,虽然一个特定的内核可能能够为每个进程处理1000多个线程,但有些可能有严格的限制,而其他问题可能会遇到其他问题,每个进程的线程数较高;您必须参考OS/CPU规格,了解您可能具有的限制。由于大多数线程都处于TIMED_WAIT状态(Java的Timer()调用Object.wait(long)方法),因此绝大多数线程的生命周期仍然以非常大的方式影响CPU ?

否(被阻塞的线程的一部分),但要考虑的事情:如果(边缘情况)所有(或> 50%)的线程需要同时运行?如果你只有几个线程管理你的软件包,那可能不是问题,但是说你有500+; 250线程同时被唤醒将导致大量的CPU争用。

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

希望有所帮助。

+0

很好的解释。 – hakunami