问题记录:
jython 运行某个脚本不会退出,通过jstack发现主线程等在Thread.join()上:
"MainThread" prio=10 tid=0x000000005891a000 nid=0x7d6d in Object.wait() [0x0000000041f5d000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000000f7c50b18> (a org.python.core.FunctionThread)
at java.lang.Thread.join(Thread.java:1186)
- locked <0x00000000f7c50b18> (a org.python.core.FunctionThread)
at java.lang.Thread.join(Thread.java:1239)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java:186)
at org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java:204)
at org.python.core.PyObject.__call__(PyObject.java:403)
at org.python.core.PyObject.__call__(PyObject.java:407)
at org.python.core.PyMethod.__call__(PyMethod.java:121)
at threading$py.join$22(/duitang/dist/sys/jython/Lib/threading.py:126)
at threading$py.call_function(/duitang/dist/sys/jython/Lib/threading.py)
at org.python.core.PyTableCode.call(PyTableCode.java:165)
at org.python.core.PyBaseCode.call(PyBaseCode.java:301)
at org.python.core.PyBaseCode.call(PyBaseCode.java:127)
at org.python.core.PyFunction.__call__(PyFunction.java:347)
at org.python.core.PyMethod.__call__(PyMethod.java:121)
at threading$py._MainThread__exitfunc$40(/duitang/dist/sys/jython/Lib/threading.py:252)
at threading$py.call_function(/duitang/dist/sys/jython/Lib/threading.py)
at org.python.core.PyTableCode.call(PyTableCode.java:165)
at org.python.core.PyBaseCode.call(PyBaseCode.java:301)
at org.python.core.PyBaseCode.call(PyBaseCode.java:194)
at org.python.core.PyFunction.__call__(PyFunction.java:417)
at org.python.core.PyMethod.instancemethod___call__(PyMethod.java:232)
at org.python.core.PyMethod.__call__(PyMethod.java:223)
at org.python.core.PyMethod.__call__(PyMethod.java:218)
at org.python.core.PyObject._callextra(PyObject.java:543)
at atexit$py._run_exitfuncs$1(/duitang/dist/sys/jython/Lib/atexit.py:34)
at atexit$py.call_function(/duitang/dist/sys/jython/Lib/atexit.py)
at org.python.core.PyTableCode.call(PyTableCode.java:165)
at org.python.core.PyBaseCode.call(PyBaseCode.java:120)
at org.python.core.PyFunction.__call__(PyFunction.java:337)
at org.python.core.PyFunction.__call__(PyFunction.java:332)
at org.python.core.PySystemState.callExitFunc(PySystemState.java:555)
at org.python.util.PythonInterpreter.cleanup(PythonInterpreter.java:344)
at org.python.util.jython.run(jython.java:383)
at org.python.util.jython.main(jython.java:137)
查看thread代码发现,进程退出的时候会通过atexit触发__exitfunc,内部实现就是等待所有非Daemon执行完毕。
class _MainThread(Thread): def __init__(self): Thread.__init__(self, name="MainThread") import atexit atexit.register(self.__exitfunc) def _create_thread(self): return java.lang.Thread.currentThread() def _set_daemon(self): return False def __exitfunc(self): _unregister_thread(self._thread) t = _pickSomeNonDaemonThread() while t: t.join() t = _pickSomeNonDaemonThread()
通过代码定位发现是stomp启动时会启动一个线程开启read_loop,代码如下:
def start(self): """ Start the connection. This should be called after all listeners have been registered. If this method is not called, no frames will be received by the connection. """ self.__running = True self.__attempt_connection() thread = self.create_thread_fc(self.__receiver_loop) self.__notify('connecting')
通过jstack也可以看到:
"Thread-1" prio=10 tid=0x00000000589e8800 nid=0x7d8f runnable [0x0000000041638000]
java.lang.Thread.State: RUNNABLE
at sun.nio.ch.FileDispatcher.read0(Native Method)
at sun.nio.ch.SocketDispatcher.read(SocketDispatcher.java:21)
at sun.nio.ch.IOUtil.readIntoNativeBuffer(IOUtil.java:198)
at sun.nio.ch.IOUtil.read(IOUtil.java:171)
at sun.nio.ch.SocketChannelImpl.read(SocketChannelImpl.java:243)
- locked <0x00000000f7c43b30> (a java.lang.Object)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java:186)
at org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java:204)
at org.python.core.PyObject.__call__(PyObject.java:420)
at org.python.core.PyObject.__call__(PyObject.java:424)
at org.python.core.PyMethod.__call__(PyMethod.java:136)
at socket$py._do_read_nio$49(/duitang/dist/sys/jython/Lib/socket.py:471)
at socket$py.call_function(/duitang/dist/sys/jython/Lib/socket.py)
at org.python.core.PyTableCode.call(PyTableCode.java:165)
at org.python.core.PyBaseCode.call(PyBaseCode.java:149)
at org.python.core.PyFunction.__call__(PyFunction.java:357)
at org.python.core.PyMethod.__call__(PyMethod.java:136)
at socket$py.read$52(/duitang/dist/sys/jython/Lib/socket.py:486)
at socket$py.call_function(/duitang/dist/sys/jython/Lib/socket.py)
at org.python.core.PyTableCode.call(PyTableCode.java:165)
at org.python.core.PyBaseCode.call(PyBaseCode.java:149)
at org.python.core.PyFunction.__call__(PyFunction.java:357)
at org.python.core.PyMethod.__call__(PyMethod.java:136)
at socket$py.recv$153(/duitang/dist/sys/jython/Lib/socket.py:1327)
at socket$py.call_function(/duitang/dist/sys/jython/Lib/socket.py)
at org.python.core.PyTableCode.call(PyTableCode.java:165)
at org.python.core.PyBaseCode.call(PyBaseCode.java:301)
at org.python.core.PyFunction.function___call__(PyFunction.java:406)
at org.python.core.PyFunction.__call__(PyFunction.java:401)
at org.python.core.PyFunction.__call__(PyFunction.java:396)
at org.python.core.PyObject._callextra(PyObject.java:543)
at socket$py.map_exception$27(/duitang/dist/sys/jython/Lib/socket.py:171)
at socket$py.call_function(/duitang/dist/sys/jython/Lib/socket.py)
at org.python.core.PyTableCode.call(PyTableCode.java:165)
at org.python.core.PyBaseCode.call(PyBaseCode.java:301)
at org.python.core.PyFunction.function___call__(PyFunction.java:406)
at org.python.core.PyFunction.__call__(PyFunction.java:401)
at org.python.core.PyFunction.__call__(PyFunction.java:396)
at org.python.core.PyObject._callextra(PyObject.java:543)
at socket$py.set_last_error$29(/duitang/dist/sys/jython/Lib/socket.py:183)
at socket$py.call_function(/duitang/dist/sys/jython/Lib/socket.py)
at org.python.core.PyTableCode.call(PyTableCode.java:165)
at org.python.core.PyBaseCode.call(PyBaseCode.java:301)
at org.python.core.PyBaseCode.call(PyBaseCode.java:141)
at org.python.core.PyFunction.__call__(PyFunction.java:357)
at org.python.core.PyMethod.__call__(PyMethod.java:136)
at stomp.connect$py._Connection__read$30(/duitang/dist/app/main/java/japa/src/main/webapp/stomp/connect.py:851)
at stomp.connect$py.call_function(/duitang/dist/app/main/java/japa/src/main/webapp/stomp/connect.py)
at org.python.core.PyTableCode.call(PyTableCode.java:165)
at org.python.core.PyBaseCode.call(PyBaseCode.java:134)
at org.python.core.PyFunction.__call__(PyFunction.java:347)
at org.python.core.PyMethod.__call__(PyMethod.java:121)
at stomp.connect$py._Connection__receiver_loop$28(/duitang/dist/app/main/java/japa/src/main/webapp/stomp/connect.py:756)
at stomp.connect$py.call_function(/duitang/dist/app/main/java/japa/src/main/webapp/stomp/connect.py)
改进措施:
mqclient新增一个atexit.register(mqclient.stop)
总结:
1. 后台线程需要管理好自己的生命周期,提供stop接口
2. 线程应该daemon还是非daemon需要考虑一下,一般不可打断的线程应该设计为非daemon的,通过exit钩子退出。