[讨论] 请教一下如何控制JVM中的线程

michael9527 2012-02-03
我需要在运行时,获取到正在运行的所有线程,JVM自身的那些线程不需要。
然后对这些线程进行一些操作,比如暂停,休眠等。
通过JMX可以拿到所有线程,但是无法对这些线程进行休眠,等待操作,Thread类里面那些wait(),sleep()方法都是针对当前线程的。就算我拿到其他线程的引用还是无法操作他们。请问有什么好的办法呢?
RednaxelaFX 2012-02-03
如果你在想做这种事情,你多半在尝试做你不应该从Java程序做的事。

但,如果你非要做的话,当然不是没办法。Thread上有suspend()和resume()方法,虽然已经是deprecated,但还存在,也还可以调用,只是不保证安全。原本这两个方法被deprecate就是因为它们的使用方式有潜在死锁的可能。

拿到所有线程之后,对你要暂停的线程调用suspend()就可以把它们停下来;回头对它们调用resume()就能恢复它们的执行。注意别把自己(当前线程)给suspend()了,你总得留个活口。

再提醒一次,虽然你可以写出代码做这件事情,但这样做就是给自己挖坑;一旦依赖上这俩方法,回头出现诡异的问题修起来就麻烦了。
michael9527 2012-02-03
呵呵,当时看了API就没敢用,万一出现死锁了就惨了。
您说的“半在尝试做你不应该从Java程序做的事”
意思是这种事情只有用C才能做到吗?
昨天我看了一下JPDA方面的介绍,似乎用底层一点的C可以做到,不过我已经不会C了
除了这两个被deprecate的方法之外,还有其他办法吗?
RednaxelaFX 2012-02-03
不是说C能做而Java不能,是你已经在用Java这种相对高层的语言,原则上应该少做危险的操作。
除了suspend()/resume(),没有别的现成的办法从Java代码控制所有Java线程的运行状态。

===============================

有一种很猥琐的办法,就是写个agent(JVMTI agent或者java.lang.instrument agent都行),拦截类加载,在类被真的加载之前改写里面的字节码,
1、找到所有循环末尾和方法结束(主要是正常返回,视情况看抛异常的地方是否也要包括)
2、在这些位置插入一些代码,检测一些你自己定义的条件,并在条件指定这个线程要暂停时自己调用wait()
3、另外写个类包装着这些条件。当需要恢复线程的执行时notify()它们。

这种办法必然会影响执行效率,不过至少可以纯Java实现,而且不会像suspend()/resume()那样不安全。

===============================

其实问题的关键点是,你原本的目的是什么。或许要达到你的目的并不需要控制所有线程暂停啊啥的。
michael9527 2012-02-03
我的目的是在一个后台线程中检查某一个时间段整个JVM进程的CPU占用率,如果CPU占用率比较高,比如超过了60%或者70%,就将web容器中的一些线程给“暂停”,不是暂停所有的线程,只暂停线程池里面的线程,也就是跑着应用的那些线程,然后“暂停”个10分钟再恢复他们。
RednaxelaFX 2012-02-03
我知道你想做的是什么了。其实之前我这边也收到过类似的需求。

例如说有一个需求是,要Java线程支持透明的超时功能,一旦该线程执行时间超过一个阈值就把该线程杀掉,无论该Java线程在执行什么。需求的重点是应用代码不需要为这种超时功能改变自己的代码。
假如有多个“应用”要在同一个JVM里跑,而且想要做所谓的“应用隔离”,也就是一个应用的毛病不要影响到另一个应用,那么像这样的需要还是有合理性的。

可惜,我没做出来。要检查超时挺容易的,但最终该如何安全的杀掉一个Java线程?用Thread.stop()也不安全。不知道有啥好办法呢…有大牛给支几招就好了
michael9527 2012-02-03
RednaxelaFX谦虚了,您就是大牛。
我们的应用不是单个JVM,而是多个JVM,每个JVM跑一个应用。一台物理机器上会有多个JVM同时在跑。但如果某个JVM上的应用CPU占用率过高,此时应该做一些限制,目前的想法没说要杀掉,就是休眠,暂停一下
然后过一段时间恢复。

相比一个JVM里面跑多个应用的情况,这种一个JVM对应一个应用应该好做一些。不过连RednaxelaFX大侠也没做出啦,看来我是没办法了,实在不行就用最笨最简单的方式,把这个应用的JVM杀掉,过几分钟再启动。
RednaxelaFX 2012-02-03
杀JVM的话容易做,不过就是你得确保你的程序不会在执行过程中向外界泄漏脏数据。例如说不会向数据库写了后头还要修改的临时数据,之类的。

其实之前我也是建议这边的某个东西改为用多个JVM的,让进程做自然隔离。可是那边不干,我也没辙
michael9527 2012-02-03
RednaxelaFX如果按您说的用java.lang.instrument agent去实现的话,如果某个线程已经处于死循环中了,怎么办呢?

比如A线程 执行了一个while(true){},这时主线程可以对他进行suspend(),stop()等,但是都有风险,相对比较安全的是让他自己执行sleep和wait。可此时这个线程自己是没办法sleep或者wait了。那么即便我改了字节码也没用,在方法开头,结尾,或者新加一个方法。都不管用了,A线程根本不会执行到那段代码了。

这个问题还真不好办啊
RednaxelaFX 2012-02-03
michael9527 写道
RednaxelaFX如果按您说的用java.lang.instrument agent去实现的话,如果某个线程已经处于死循环中了,怎么办呢?

我不是说了要在循环末尾方法结束的地方插入检查么。死循环再怎么死也要从后向前跳转才能形成循环。所以我说的检查是可以覆盖这种情况的。
while (true) { }

经过instrument后会变成
while (true) {
  if (MyCondition.shouldStopThisThread(Thread.currentThread())) {
    Thread.currentThread().wait();
  }
}


实际上就是照抄HotSpot VM所选择的safepoint的安插位置而已。
Global site tag (gtag.js) - Google Analytics