我有一个线程池(executor),我想监视它是否过度使用资源(由于cpu和内存似乎很难)。我想"杀死"运行时间过长的线程,就像杀死一个操作系统进程一样。工人们花了大部分时间计算,但也花了大量时间等待I/O,主要是数据库。。。
我一直在阅读关于停止java中的线程的文章,以及它是如何因资源清理原因(未正确释放锁、关闭套接字和文件等)而被弃用的。建议的方法是定期检查工作线程是否应该停止,然后退出。这显然期望客户端线程以特定的方式编写,并且它们在等待某些外部I/O时不会被阻塞。还有ThreadDeth和InterruptedException,它们可能能够完成这项工作,但它们实际上可能在编写不当/恶意的工作线程中被规避,而且我得到的印象是(尽管还没有测试),当工作线程等待I/O时,它们Interrupted Exception在某些(甚至所有)情况下可能无法正常工作。
缓解这种情况的另一种方法是使用多个操作系统进程来隔离系统的各个部分,但这会带来一些不必要的资源消耗增加。
这让我想起了五年多前关于隔离物和/或MVM的老故事,但在这方面似乎什么都没有发生,也许在java 8或9中…
所以,实际上,这一切让我怀疑,是否可以通过使用每个线程都有自己的类加载器来实现一些穷人对进程的模拟?如果每个线程(或组)都被加载到自己的类加载器中,那么这可以用来模拟进程吗?我不确定这会带来多大的资源消耗增加(因为不会有太多的代码共享,代码也不小)。至少进程写时复制语义支持代码共享。。
有什么建议/想法吗?
编辑:
我之所以提出这个问题,是因为到目前为止,JVM中还没有这种解决方案(我的意思是,共享应用程序服务器实际上是不可能的——.NET中的应用程序域或类似的东西似乎正是解决了这种问题)。我知道杀死一个进程并不能保证将所有系统状态恢复到某个初始状态,但至少释放了句柄、内存和cpu等所有资源。我考虑使用类加载器,因为它们可能有助于释放线程持有的锁,这也是thread.stop被弃用的原因之一。在我目前的情况下,唯一应该发布的(我目前能想到的)是数据库连接,如果需要,可以单独/外部(由看门狗线程)处理。。虽然,实际上,在我的情况下,Thread.stop实际上可能是可行的,但我只是不喜欢使用不推荐使用的方法。。
此外,我认为这是一个针对不当行为流程的安全网,理想情况下,它们应该表现良好,并且在很大程度上处于我的控制之下。
因此,为了澄清,我想问的是,例如,服务器端的java人员如何处理失控的线程?我怀疑是通过使用集群中的许多机器来解决问题,并重新启动行为不端的机器——至少在应用程序处于无状态时。。
线程和进程之间的区别在于,线程隐式地共享内存和资源,如套接字和文件(使线程本地内存成为一种解决方法)。进程隐含地具有私有内存和资源。
杀死线程不是问题。问题是,一个行为不好的线程,甚至一个行为合理的线程,都会使资源处于不一致的状态。使用类加载器不会帮助您跟踪这一点,也不会为您解决问题。对于流程来说,跟踪它们正在使用的资源更容易,因为大多数资源都是孤立的。即使对于进程,如果被杀死,它们也可能使锁、临时文件和共享IPC资源处于不正确的状态。
真正的解决方案是编写行为正常的代码,以便对其进行管理和处理,并试图处理每一个可能的行为不佳的代码几乎是不可能的。如果你有一个糟糕的第三方库,你必须使用,你可以尝试杀死并清理它,你可以想出一个好的解决方案,但你不能指望它是一个干净的解决方案。
编辑:这里有一个简单的程序,它会在两个进程或机器之间死锁,因为它有一个错误。阻止死锁的方法是修复代码。
public static void main(String... args) throws IOException {
switch(args.length) {
case 1: {
// server
ServerSocket ss = new ServerSocket(Integer.parseInt(args[0]));
Socket s = ss.accept();
ObjectInputStream ois = new ObjectInputStream(s.getInputStream());
ObjectOutputStream oos = new ObjectOutputStream(s.getOutputStream());
// will deadlock before it gets here
break;
}
case 2: {
Socket s = new Socket(args[0], Integer.parseInt(args[1]));
ObjectInputStream ois = new ObjectInputStream(s.getInputStream());
ObjectOutputStream oos = new ObjectOutputStream(s.getOutputStream());
// will deadlock before it gets here
break;
}
default:
System.err.println("Must provide either a port as server or hostname port as client");
}
}