How to handle multiple threads in Java?

I am trying to start a process and do stuff with input, output and error streams. The obvious way to do this is to use something like select() , but the only thing I can find in Java that does this is Selector.select() , which accepts Channel . It seems impossible to get a Channel from InputStream or OutputStream ( FileStream has a getChannel() method, but that doesn't help here)

So, instead, I wrote code to poll all threads:

 while( !out_eof || !err_eof ) { while( out_str.available() ) { if( (bytes = out_str.read(buf)) != -1 ) { // Do something with output stream } else out_eof = true; } while( err_str.available() ) { if( (bytes = err_str.read(buf)) != -1 ) { // Do something with error stream } else err_eof = true; } sleep(100); } 

which works, except that it never ends. When one of the threads reaches the end of the file, available() returns zero, so read() not called, and we never get a return of -1, which points to EOF.

One solution would be a non-blocking method of detecting EOF. I do not see it anywhere in the documents. Also, is there a better way to do what I want to do?

I see this question here: link text and although this is not quite what I want, I can probably use this idea by multiplying separate threads for each thread for the specific problem that I have now. But of course, is this not the only way to do this? Surely there should be a way to read from multiple threads without using a thread for each?

+4
source share
2 answers

As you said, the solution outlined in this answer is the traditional way of reading both stdout and stderr from a process. Flow for flow is the way, although it is a little annoying.

+4
source

You really have to go the way of creating a thread for each thread that you want to control. If your use case allows you to combine both stdout and stderr of the process in question, you only need one thread, otherwise two are needed.

It took me some time to understand this in one of our projects, where I have to start an external process, release it and do something with it, at the same time look for errors and terminate the process, as well as to stop it when the application user java cancels the operation.

I created a fairly simple class to encapsulate the observing part, whose run () method looks something like this:

 public void run() { BufferedReader tStreamReader = null; try { while (externalCommand == null && !shouldHalt) { logger.warning("ExtProcMonitor(" + (watchStdErr ? "err" : "out") + ") Sleeping until external command is found"); Thread.sleep(500); } if (externalCommand == null) { return; } tStreamReader = new BufferedReader(new InputStreamReader(watchStdErr ? externalCommand.getErrorStream() : externalCommand.getInputStream())); String tLine; while ((tLine = tStreamReader.readLine()) != null) { logger.severe(tLine); if (filter != null) { if (filter.matches(tLine)) { informFilterListeners(tLine); return; } } } } catch (IOException e) { logger.logExceptionMessage(e, "IOException stderr"); } catch (InterruptedException e) { logger.logExceptionMessage(e, "InterruptedException waiting for external process"); } finally { if (tStreamReader != null) { try { tStreamReader.close(); } catch (IOException e) { // ignore } } } } 

On the caller, it looks like this:

  Thread tExtMonitorThread = new Thread(new Runnable() { public void run() { try { while (externalCommand == null) { getLogger().warning("Monitor: Sleeping until external command is found"); Thread.sleep(500); if (isStopRequested()) { getLogger() .warning("Terminating external process on user request"); if (externalCommand != null) { externalCommand.destroy(); } return; } } int tReturnCode = externalCommand.waitFor(); getLogger().warning("External command exited with code " + tReturnCode); } catch (InterruptedException e) { getLogger().logExceptionMessage(e, "Interrupted while waiting for external command to exit"); } } }, "ExtCommandWaiter"); ExternalProcessOutputHandlerThread tExtErrThread = new ExternalProcessOutputHandlerThread("ExtCommandStdErr", getLogger(), true); ExternalProcessOutputHandlerThread tExtOutThread = new ExternalProcessOutputHandlerThread("ExtCommandStdOut", getLogger(), true); tExtMonitorThread.start(); tExtOutThread.start(); tExtErrThread.start(); tExtErrThread.setFilter(new FilterFunctor() { public boolean matches(Object o) { String tLine = (String)o; return tLine.indexOf("Error") > -1; } }); FilterListener tListener = new FilterListener() { private boolean abortFlag = false; public boolean shouldAbort() { return abortFlag; } public void matched(String aLine) { abortFlag = abortFlag || (aLine.indexOf("Error") > -1); } }; tExtErrThread.addFilterListener(tListener); externalCommand = new ProcessBuilder(aCommand).start(); tExtErrThread.setProcess(externalCommand); try { tExtMonitorThread.join(); tExtErrThread.join(); tExtOutThread.join(); } catch (InterruptedException e) { // when this happens try to bring the external process down getLogger().severe("Aborted because auf InterruptedException."); getLogger().severe("Killing external command..."); externalCommand.destroy(); getLogger().severe("External command killed."); externalCommand = null; return -42; } int tRetVal = tListener.shouldAbort() ? -44 : externalCommand.exitValue(); externalCommand = null; try { getLogger().warning("command exit code: " + tRetVal); } catch (IllegalThreadStateException ex) { getLogger().warning("command exit code: unknown"); } return tRetVal; 

Unfortunately, I don't need to use a standalone runnable example, but maybe this helps. If I had to do this again, I would once again look at using the Thread.interrupt () method instead of the self-starting stop flag (mind to declare it volatile!), But I leave it at a different time. :)

+2
source

Source: https://habr.com/ru/post/1277106/


All Articles