Java Theory and Practice: Hey, where is my thread?

xiaoxiao2021-03-06  102

If you are not careful, the thread may disappear from the server application without (stack) tracking. In this article, thread problems Brian Goetz provide techniques for preventing and detecting threads "Good Left Professionals".

When the primary thread in a single-threaded application throws an unprepired exception, you will not notice that the stack tracking is printed in the console (also because the program is stopped). However, in multi-threaded applications, especially in applications run as a server and not connected to the console, thread death may become less eye-catching events, which will cause local system failure, thus generating confusion applications. behavior.

In Java THEORY AND PRACTICE, we studied thread pools and studied how the incorrect thread pool will "leak" thread until all threads will eventually lose. Most thread pools achieve this by capturing exceptions or restarting threads from death, but thread leakage problems are not limited to thread pools - using threads to serve the work queue to serve the work queue. problem. When the server application lost a working thread (Worker Thread), the application may still be normal, which makes the real reason for this problem difficult to determine.

Many applications provide background services - handle tasks from event queues, read commands from sockets or perform long-term tasks other than the UI thread. What happens what happens when the I / O operation of these threads is caused by throwing unsucked RuntimeException or Error, or only stopped, or when it is not expected to blocked it.

Sometimes, if the thread performs long-term tasks (such as spell check) started by the user (such as spell check), the user noticed that the task has no progress, and they may stop the operation or program. But other times, the background thread performs the "Cleaning Maintenance" task, which may disappear for a long time and not perceived.

The sample server application considers such a hypothetical middleware server application that aggregates messages from various input sources, then submit them to an external server application, receive responses from the external application and respond to the appropriate input source . For each input source, there is a plugin that accepts its input messages in its own way (by scanning file directory, waiting for socket connection, polling database table, etc.). The plugin can be written by a third party, even if they are running on the server JVM. This application has (at least) two internal work queues - a message that is waiting to be sent to the server from the plugin ("Outbound Message" queue), and the response from the server is waiting to be passed to the appropriate plugin ( "Inbound Response" queue). By calling the service routine incomingResponse () on the plugin object, the message is routed to the plugin that originally issued a request.

After receiving the message from the plugin, it is arranged in the outbound message queue. The message in the outbound message queue is processed by one or more threads from the queue to record its source and submit it to the remote server application (assuming through the web service interface). The remote application eventually returns respond via the web service interface, and then our server is arranged in the inbound response queue. One or more responding threads read the message from the inbound response queue and route it to the appropriate plug-in to complete the round trip "journey".

In this application, there are two message queues, which are used for outbound requests and inbound responses, and there may also have another queue in different plugins. We also have several service threads, one reads the request from the outbound message queue and submits it to the external server, and a slave response queue read response and routes it to the plugin, in the socket or other There may also be some threads in plugins for external request sources. If the thread fails, it is not always obvious if one of these threads (such as response distribution threads) disappear, what will happen? Because the plug-in can be submitted to new messages, they may not immediately notice that some respects have been wrong. The message will still arrive through various input sources and submitted to external services through our application. Because the plugin is not expected to get its response immediately, it still doesn't realize the problem. Finally, the received response will be full of queues. If they are stored in memory, then they will eventually be exhausted. Even if you don't deplete the memory, someone will find a response at a time that the response is not passed - but this may take some time because the system can still function properly.

When the main task processing is processed by the thread pool instead of a single thread, the consequences of accidental thread leaks have a certain degree of protection, because an eight-threaded thread pool is performed, complete its work with seven threads. The efficiency may still be accepted. At first, there may be no significant differences. However, system performance will eventually fall, although this decrease is not perceived.

The thread leak problem in the server application is not always easily detected from the outside. Because most threads only process partial workloads of the server, or may only process a specific type of daemon task, it seems that it seems to work properly when the program actually encounters a serious failure. This, plus the factors that cause thread leaks do not always leave significant traces, will cause surprising or confusing applications.

RuntimeException is a primary reason that causes thread death. When the thread throws unpowed exceptions or errors, they may disappear; while the I / O operation waiting for thread will never be completed, or when there is no one to wait for the monitor to call NOTIFY () They just stop working. The most common root of accident threads is RuntimeException, such as NullPointerexception, ArrayIndexOfboundSexception, etc.). In our sample application, the RuntimeException may be thrown when INCOSPONSE () on the plugin object is transferred to the plugin. The plugin code may be written by a third party or may be written after writing the application, so the application writer cannot review its correctness. If some plugins throw RuntimeException, some response service threads will terminate, which means an error-sized plugin will make the entire system crash. Unfortunately, this vulnerability is very common.

Although the compiler "I hope" we took the initiative to compile code (compiler to force us to do this), most Java developers have largely ignored unrecognized exceptions. In a single-threaded application, the result of unprocessed runtimeException is obvious, and a clear stack tracking of an abnormal position, which provides a problem notification and a useful information to solve the problem. However, in multi-threaded applications, the thread will be silently died because of the abnormalities that are not found - so that users and developers have no shortage of problems with the problems and why they have occurred.

Handling the thread of the task (similar to the request and response handler in the sample application), basically spending the entire lifecycle through a abstract obstacle similar to Runnable to call the service method. Because we don't know what is on the other side of this abstract obstacle, so for service methods, we should doubt, it is really good to suppose that it never throws an extent of unusual. If the service routine throws RuntimeException, call the thread should capture this exception and record it to the log, and then go to the next or turn off the thread in the queue and then restart it. (The latter option is derived from this: Any code that throws RuntimeException or Error may have destroyed the state of the thread.) The code in Listing 1 is a typical thread from the work queue processing Runnable task, similar to our example Inbound response threads in the middle. It does not prevent any plugins that do not have an exception.

Listing 1. Do not prevent RuntimeException work thread

Private class trustingpoolworker extends thread {

Public void run () {

IncomingResponse IR;

While (true) {

Ir = (incomingResponse) queue.getnext ();

Plugin Plugin = FindPlugin (Ir.getResponseid ());

IF (Plugin! = NULL)

Plugin.handleMessage (Ir.GetResponse ());

Else

LOG ("Unknown Plug-in for Resonse" Ir.getResponseid ());

}

}

}

We don't have to add a lot of code to make this working thread to more robust the fault in the plugin code. Just capture runtimeException, then corrective operations, you can ensure that we have the ability to prevent a plurality of plugins that have a poor plug-in to destroy the entire server. Appropriate correction should record the error to the log, then simply go to the next message, terminate the current thread and restart it (this is similar to Timertask classes), or uninstall the plugin that causes problems, such as list 2 Distance shown in:

Listing 2. Prevention of RuntimeException work threads

Private class saffrworker extends thread {

Public void run () {

IncomingResponse IR;

While (true) {

Ir = (incomingResponse) queue.getnext ();

Plugin Plugin = FindPlugin (Ir.getResponseid ());

IF (Plugin! = null) {

Try {

Plugin.handleMessage (Ir.GetResponse ());

}

Catch (runtimeexception e) {

// Take Some Sort of Action;

// - log the exception and move on

// - log the Exception and Restart The Worker Thread

// - log the eXception and unload the offending plug-in

}

}

Else

LOG ("Unknown Plug-in for Resonse" Ir.getResponseid ());

}

}

}

The UncaughTexception function using the ThreadGroup class was also wise from the method of using the ThreadGroup class using the uncapting abnormal handler provided by ThreadGroup as a method of throwing RuntimeException. Threadgroup is not very large, but currently (until the Thread processed in JDK 1.5 adds uncaled exception handling), the uncaughteException feature is temporarily indispensable. Listing 3 shows an example, using ThreadGroup to detect thread death caused by unrecovered exceptions. Listing 3. Using UncaughTexception to detect thread death

Public class threadgroupexample {

Public static class mythreadgroup extends threadgroup {

Public mythreadgroup (string s) {

Super (s);

}

Public void UncaughTexception (thread thread, throwable throwable) {

System.out.println ("thread" thread.getname ()

"Died, Exception WAS:");

Throwable.printStackTrace ();

}

}

Public static threadgroup workerThreads =

New MythreadGroup ("Worker Threads");

Public Static Class Workerthread Extends Thread {

Public workerthread (string s) {

Super (Workerthreads, s);

}

Public void run () {

Throw new runtimeException ();

}

}

Public static void main (String [] args) {

Thread T = New Workerthread ("Worker Thread");

T.Start ();

}

}

If one of the threads in the thread group dies by throwing an unpreciated exception, call the UNCAUGHTEXCEPTION () method of the thread group, which can write a record to the log, restart the thread, then restart the system, or take it It considers any correct or diagnostic operations. At least, if all threads write a log message while thread death, you will have a record, instead of only why you can deduce the thread where you want to handle the thread.

Conclusion When the thread disappears from the application, it will cause confusion, and in many cases, there is no (stack) tracking when thread disappears. Like a lot of risks, the best way to prevent thread leakage is to prevent and detect the combination of detection; note that there is a place where RuntimeException (such as calling foreign code) and uses ThreadGroup's UncaughTexception handler to terminate at the thread Perform testing.

转载请注明原文地址:https://www.9cbs.com/read-99438.html

New Post(0)