Pho*_*reo 15 java performance multithreading timer
我的问题:
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?
txt*_*elp 25
首先,Java(或.NET)线程!=内核/ OS线程.
Java Thread是一个高级包装器,它抽象出系统线程的一些功能; 这些类型的线程也称为托管线程.在内核级别,线程只有2个状态,运行且未运行.内核跟踪的是一些管理信息(堆栈,指令指针,线程ID等),但是在内核级别没有像处于某种TIMED_WAITING状态的线程那样的东西(.NET等同于WaitSleepJoin状态) .那些"状态"只存在于这些类型的上下文中(C++ std::thread没有state成员的部分原因).
话虽如此,当托管线程被阻止时,它会以两种方式完成(取决于如何在托管级别阻止它被阻止); 我在OpenJDK中看到的用于线程代码的实现利用信号量来处理托管等待(这是我在其他C++框架中看到的,它有一些"托管"线程类以及.NET Core库),并将互斥锁用于其他类型的等待/锁定.
由于大多数实现都会使用某种锁定机制(如信号量或互斥量),因此内核通常会做同样的事情(至少在你的问题所在的地方); 也就是说,内核将把线程从"运行"队列中取出并放入"等待"队列(上下文切换).进入线程调度,特别是内核如何处理线程的执行超出了本Q&A的范围,特别是因为您对Java有疑问,Java可以在很多不同类型的OS上运行(每个它完全不同地处理线程).
更直接地回答您的问题:
当线程处于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(a)中引用的列表或哈希映射中(或者单独的线程)以查看当前时间是否与包的到期时间匹配,然后"expire"代码将运行.这将线程数减少到1并且访问时间减少到O(1); 但是,如果没有代码,该建议可能在您的方案中不起作用.
希望有所帮助.