How to use exitValue() with parameter? - java

A very good article (When Runtime.exec() won't) says: The only possible time you would use exitValue() instead of waitFor() would be when you don't want your program to block waiting on an external process that may never complete. Instead of using the waitFor() method, I would prefer passing a boolean parameter called waitFor into the exitValue() method to determine whether or not the current thread should wait. A boolean would be more beneficial because exitValue() is a more appropriate name for this method, and it isn't necessary for two methods to perform the same function under different conditions. Such simple condition discrimination is the domain of an input parameter.
I have exactly same situation where my system call would start a process which will keep running until user decides to kill it. If I use '(process.waitFor() == 0)' it will block program there because process will not be completed. Author in article above suggest that exitValue() can be used with 'waitFor' parameter. Did anybody try it out ? Any example would be helpful.
Code:
// Start ProcessBuilder, 'str' contains a command
ProcessBuilder pbuilder = new ProcessBuilder(str);
pbuilder.directory(new File("/root/workspace/Project1"));
pbuilder.redirectErrorStream(true);
Process prcs = pbuilder.start();
AForm.execStatustext.append("\n=> Process is:" + prcs);
// Read output
StringBuilder out = new StringBuilder();
BufferedReader bfrd = new BufferedReader(new InputStreamReader(process.getInputStream()));
String current_line = null, previous_line = null;
while ((current_line = bfrd.readLine()) != null) {
if (!line.equals(previous_line)) {
previous_line = current_line;
out.append(current_line).append('\n');
//System.out.println(line);
}
}
//process.getInputStream().close();
// Send 'Enter' keystroke through BufferedWriter to get control back
BufferedWriter bfrout = new BufferedWriter(new OutputStreamWriter(prcs.getOutputStream()));
bfrout.write("\\r");
bfrout.newLine();
bfrout.flush();
bfrout.write("\\r");
bfrout.newLine();
bfrout.flush();
//process.getOutputStream().close();*/
if (prcs.waitFor() == 0)
System.out.println("Commands executed successfully");
System.exit(0);

This is a "rough" example of some library code I use to launch external processes.
Basically, this uses three threads. The first is used to execute the actually command and then wait till it exists.
The other two deal with the processes output and input streams. This makes these independent of each other prevents the ability for one to block the other.
The whole thing is then tied together with a listener that is notified when something happens.
The error handling could be better (as the fail condition is a little unclear as to what/who actually failed), but the basic concept is there...
This means you can launch the process and not care...(until you want to)
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class TestBackgroundProcess {
public static void main(String[] args) {
new TestBackgroundProcess();
}
public TestBackgroundProcess() {
BackgroundProcess bp = new BackgroundProcess("java", "-jar", "dist/BackgroundProcess.jar");
bp.setListener(new ProcessListener() {
#Override
public void charRead(BackgroundProcess process, char value) {
}
#Override
public void lineRead(BackgroundProcess process, String text) {
System.out.println(text);
}
#Override
public void processFailed(BackgroundProcess process, Exception exp) {
System.out.println("Failed...");
exp.printStackTrace();
}
#Override
public void processCompleted(BackgroundProcess process) {
System.out.println("Completed - " + process.getExitValue());
}
});
System.out.println("Execute command...");
bp.start();
bp.send("dir");
bp.send("exit");
System.out.println("I'm not waiting here...");
}
public interface ProcessListener {
public void charRead(BackgroundProcess process, char value);
public void lineRead(BackgroundProcess process, String text);
public void processFailed(BackgroundProcess process, Exception exp);
public void processCompleted(BackgroundProcess process);
}
public class BackgroundProcess extends Thread {
private List<String> commands;
private File startIn;
private int exitValue;
private ProcessListener listener;
private OutputQueue outputQueue;
public BackgroundProcess(String... cmds) {
commands = new ArrayList<>(Arrays.asList(cmds));
outputQueue = new OutputQueue(this);
}
public void setStartIn(File startIn) {
this.startIn = startIn;
}
public File getStartIn() {
return startIn;
}
public int getExitValue() {
return exitValue;
}
public void setListener(ProcessListener listener) {
this.listener = listener;
}
public ProcessListener getListener() {
return listener;
}
#Override
public void run() {
ProcessBuilder pb = new ProcessBuilder(commands);
File startIn = getStartIn();
if (startIn != null) {
pb.directory(startIn);
}
pb.redirectError();
Process p;
try {
p = pb.start();
InputStreamConsumer isc = new InputStreamConsumer(p.getInputStream(), this, getListener());
outputQueue.init(p.getOutputStream(), getListener());
outputQueue.start();
p.waitFor();
isc.join();
outputQueue.terminate();
outputQueue.join();
ProcessListener listener = getListener();
if (listener != null) {
listener.processCompleted(this);
}
} catch (InterruptedException ex) {
ProcessListener listener = getListener();
if (listener != null) {
listener.processFailed(this, ex);
}
} catch (IOException ex) {
ProcessListener listener = getListener();
if (listener != null) {
listener.processFailed(this, ex);
}
}
}
public void send(String cmd) {
outputQueue.send(cmd);
}
}
public class OutputQueue extends Thread {
private List<String> cmds;
private OutputStream os;
private ProcessListener listener;
private BackgroundProcess backgroundProcess;
private ReentrantLock waitLock;
private Condition waitCon;
private boolean keepRunning = true;
public OutputQueue(BackgroundProcess bp) {
backgroundProcess = bp;
cmds = new ArrayList<>(25);
waitLock = new ReentrantLock();
waitCon = waitLock.newCondition();
}
public ProcessListener getListener() {
return listener;
}
public OutputStream getOutputStream() {
return os;
}
public BackgroundProcess getBackgroundProcess() {
return backgroundProcess;
}
public void init(OutputStream outputStream, ProcessListener listener) {
os = outputStream;
this.listener = listener;
}
public void send(String cmd) {
waitLock.lock();
try {
cmds.add(cmd);
waitCon.signalAll();
} finally {
waitLock.unlock();
}
}
public void terminate() {
waitLock.lock();
try {
cmds.clear();
keepRunning = false;
waitCon.signalAll();
} finally {
waitLock.unlock();
}
}
#Override
public void run() {
try {
Thread.sleep(500);
} catch (InterruptedException ex) {
}
BackgroundProcess backgroundProcess = getBackgroundProcess();
ProcessListener listener = getListener();
OutputStream outputStream = getOutputStream();
try {
while (keepRunning) {
while (cmds.isEmpty() && keepRunning) {
waitLock.lock();
try {
waitCon.await();
} catch (Exception exp) {
} finally {
waitLock.unlock();
}
}
if (!cmds.isEmpty()) {
waitLock.lock();
try {
while (!cmds.isEmpty()) {
String cmd = cmds.remove(0);
System.out.println("Send " + cmd);
outputStream.write(cmd.getBytes());
outputStream.write('\n');
outputStream.write('\r');
outputStream.flush();
}
} finally {
waitLock.unlock();
}
}
}
} catch (IOException ex) {
if (listener != null) {
listener.processFailed(backgroundProcess, ex);
}
}
}
}
public class InputStreamConsumer extends Thread {
private InputStream is;
private ProcessListener listener;
private BackgroundProcess backgroundProcess;
public InputStreamConsumer(InputStream is, BackgroundProcess backgroundProcess, ProcessListener listener) {
this.is = is;
this.listener = listener;
this.backgroundProcess = backgroundProcess;
start();
}
public ProcessListener getListener() {
return listener;
}
public BackgroundProcess getBackgroundProcess() {
return backgroundProcess;
}
#Override
public void run() {
BackgroundProcess backgroundProcess = getBackgroundProcess();
ProcessListener listener = getListener();
try {
StringBuilder sb = new StringBuilder(64);
int in = -1;
while ((in = is.read()) != -1) {
char value = (char) in;
if (listener != null) {
listener.charRead(backgroundProcess, value);
if (value == '\n' || value == '\r') {
if (sb.length() > 0) {
listener.lineRead(null, sb.toString());
sb.delete(0, sb.length());
}
} else {
sb.append(value);
}
}
}
} catch (IOException ex) {
listener.processFailed(backgroundProcess, ex);
}
}
}
}

Before using waitFor in main thread, create another thread (child) and construct logic for your termination cases in this new thread. For example, wait for 10 secs.
If the condition is fulfilled, then interrupt the main thread from the child thread ant handle the following logic on your main thread.
The following code creates a child thread to invoke the process and the main thread does its work until the child finishes successfully.
import java.io.IOException;
public class TestExecution {
public boolean myProcessState = false;
class MyProcess implements Runnable {
public void run() {
//------
Process process;
try {
process = Runtime.getRuntime().exec("your command");
process.waitFor();
int processExitValue = process.exitValue();
if(processExitValue == 0) {
myProcessState = true;
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void doMyWork() {
MyProcess myProcess = new MyProcess();
Thread myProcessExecuter = new Thread(myProcess);
myProcessExecuter.start();
while(!myProcessState) {
// do your job until the process exits with success
}
}
public static void main(String[] args) {
TestExecution testExecution = new TestExecution();
testExecution.doMyWork();
}
}

If I use '(process.waitFor() == 0)' it will block program there because process will not be completed.
No it won't. It will block the thread. That's why you have threads.
Author in article above suggest that exitValue() can be used with 'waitFor' parameter
No he doesn't. He is talking about how he would have designed it, if anybody had asked him. But they didn't, and he didn't.
Did anybody try it out ?
You can't. It doesn't exist.

Related

Multiple Thread Writer with Single Thread Reader using PipedOutputStream and PipedInputStream

I have a Multiple Writer Threads with Single Reader Thread model.
The ThreadMultipleDateReceiver class is designed to read from multiple Threads.
public class ThreadMultipleDateReceiver extends Thread {
private static final int MAX_CLIENT_THREADS = 4;
private byte[] incomingBytes;
private volatile boolean isRunning;
private volatile List<ThreadStreamDateWriter> lThrdDate;
private static PipedInputStream pipedInputStream;
public ThreadMultipleDateReceiver() {
lThrdDate = Collections.synchronizedList(new ArrayList<>(MAX_CLIENT_THREADS));
pipedInputStream = new PipedInputStream();
System.out.println("ThreadMultipleDateReceiver Created");
}
#Override public void run() {
isRunning = true;
while (isRunning) {
if (!lThrdDate.isEmpty()) {
System.out.println("ThreadMultipleDateReceiver has:" + lThrdDate.size());
for (int i = lThrdDate.size(); i > 0; i--) {
if (lThrdDate.get(i - 1).getState() == Thread.State.TERMINATED) {
lThrdDate.remove(i - 1);
} else {
System.out.println("I ThreadMultipleDateReceiver have:" + lThrdDate.get(i - 1).getNameDateWriter());
}
}
incomingBytes = new byte[1024];
try {
String str = "";
int iRd;
System.out.println("ThreadMultipleDateReceiver waiting:" + str);
while ((iRd = pipedInputStream.read(incomingBytes)) != -1) {
if (iRd > 0) {
str += new String(incomingBytes);
}
}
System.out.println("ThreadMultipleDateReceiver Received:\n\t:" + str);
} catch (IOException e) { }
} else {
System.out.println("ThreadMultipleDateReceiver Empty");
}
}
emptyDateWriters();
}
public void addDateWriter(ThreadStreamDateWriter threadDateWriter) {
if (lThrdDate.size() < MAX_CLIENT_THREADS) {
lThrdDate.add(threadDateWriter);
}
}
private void emptyDateWriters() {
if (!lThrdDate.isEmpty()) {
for (int i = lThrdDate.size(); i > 0; i--) {
ThreadStreamDateWriter threadDateWriter = lThrdDate.get(i - 1);
threadDateWriter.stopThread();
lThrdDate.remove(i - 1);
}
}
}
public PipedInputStream getPipedInputStream() {
return pipedInputStream;
}
public void stopThread() {
isRunning = false;
}
}
And the single Writer Thread
public class ThreadStreamDateWriter extends Thread {
String Self;
private byte[] outgoingBytes;
private volatile boolean isRunning;
private static PipedOutputStream pipedOutputStream;
ThreadStreamDateWriter(String name, PipedInputStream snk) {
Self = name;
pipedOutputStream = new PipedOutputStream();
try {
pipedOutputStream.connect(snk);
} catch (IOException e) { }
}
#Override public void run() {
isRunning = true;
while (isRunning) {
try {
outgoingBytes = getInfo().getBytes();
System.out.println("ThreadStreamDateWriter -> write to pipedOutputStream:" + new String(outgoingBytes));
pipedOutputStream.write(outgoingBytes);
System.out.println("ThreadStreamDateWriter -> wrote:" + new String(outgoingBytes));
try { Thread.sleep(4000); } catch (InterruptedException ex) { }
} catch (IOException | NegativeArraySizeException | IndexOutOfBoundsException e) {
isRunning = false;
}
}
}
String getInfo() {
String sDtTm = new SimpleDateFormat("yyyyMMdd-hhmmss").format(Calendar.getInstance().getTime());
return Self + " -> " + sDtTm;
}
public void stopThread() {
isRunning = false;
}
public String getNameDateWriter() {
return Self;
}
}
How launch (I'm using Netbeans)?
ThreadMultipleDateReceiver thrdMDateReceiver = null;
ThreadStreamDateWriter thrdSDateWriter0 = null;
ThreadStreamDateWriter thrdSDateWriter1 = null;
private void jtbDateExchangerActionPerformed(java.awt.event.ActionEvent evt) {
if (jtbDateExchanger.isSelected()) {
if (thrdMDateReceiver == null) {
thrdMDateReceiver = new ThreadMultipleDateReceiver();
thrdMDateReceiver.start();
}
if (thrdSDateWriter0 == null) {
thrdSDateWriter0 = new ThreadStreamDateWriter("-0-", thrdMDateReceiver.getPipedInputStream());
thrdSDateWriter0.start();
thrdMDateReceiver.addDateWriter(thrdSDateWriter0);
}
if (thrdSDateWriter1 == null) {
thrdSDateWriter1 = new ThreadStreamDateWriter("-1-", thrdMDateReceiver.getPipedInputStream());
thrdSDateWriter1.start();
thrdMDateReceiver.addDateWriter(thrdSDateWriter1);
}
} else {
if (thrdMDateReceiver != null) {
thrdMDateReceiver.stopThread();
}
}
}
The OUTPUT
run:
ThreadMultipleDateReceiver Created
ThreadMultipleDateReceiver Empty
ThreadMultipleDateReceiver Empty
ThreadMultipleDateReceiver Empty
.....
ThreadMultipleDateReceiver Empty
ThreadMultipleDateReceiver Empty
ThreadMultipleDateReceiver Empty
ThreadMultipleDateReceiver has:1
I ThreadMultipleDateReceiver have:-0-
ThreadMultipleDateReceiver waiting:
ThreadStreamDateWriter -> write to pipedOutputStream:-0- -> 20170608-090003
ThreadStreamDateWriter -> write to pipedOutputStream:-1- -> 20170608-090003
BUILD SUCCESSFUL (total time: 1 minute 3 seconds)
The ThreadMultipleDateReceiver is blocked, and is not printing:
ThreadMultipleDateReceiver Received:
-1- -> 20170608-090003
or
ThreadMultipleDateReceiver Received:
-0- -> 20170608-090003
How solve it?
looks like your piped output stream is static, so every time you construct a ThreadStreamDateWriter, you are stepping on the old value of piped output stream.
try making this an instance variable and pass it into the constructor. so you only have one of them.
edit 1: i made the pipes instance variables and added some printouts. seems to be running longer now (see below):
edit 2: you second pipedOutputStream.connect(snk); is throwing. you can only connect one thing at a time.
import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.List;
public class So44438086 {
public static class ThreadMultipleDateReceiver extends Thread {
private static final int MAX_CLIENT_THREADS=4;
private byte[] incomingBytes;
private volatile boolean isRunning;
private volatile List<ThreadStreamDateWriter> lThrdDate;
private /*static*/ PipedInputStream pipedInputStream;
public ThreadMultipleDateReceiver() {
lThrdDate=Collections.synchronizedList(new ArrayList<>(MAX_CLIENT_THREADS));
pipedInputStream=new PipedInputStream();
System.out.println("ctor setting pipedInputStream to: "+pipedInputStream);
System.out.println("ThreadMultipleDateReceiver Created");
}
#Override public void run() {
isRunning=true;
while(isRunning) {
if(!lThrdDate.isEmpty()) {
System.out.println("ThreadMultipleDateReceiver has:"+lThrdDate.size());
for(int i=lThrdDate.size();i>0;i--) {
if(lThrdDate.get(i-1).getState()==Thread.State.TERMINATED) {
lThrdDate.remove(i-1);
} else {
System.out.println("I ThreadMultipleDateReceiver have:"+lThrdDate.get(i-1).getNameDateWriter());
}
}
incomingBytes=new byte[1024];
try {
String str="";
int iRd;
System.out.println("ThreadMultipleDateReceiver waiting:"+str);
System.out.println("reading: "+pipedInputStream);
while((iRd=pipedInputStream.read(incomingBytes))!=-1) {
if(iRd>0) {
str+=new String(incomingBytes);
}
}
System.out.println("ThreadMultipleDateReceiver Received:\n\t:"+str);
} catch(IOException e) {}
} else {
System.out.println("ThreadMultipleDateReceiver Empty");
}
}
emptyDateWriters();
}
public void addDateWriter(ThreadStreamDateWriter threadDateWriter) {
if(lThrdDate.size()<MAX_CLIENT_THREADS) {
lThrdDate.add(threadDateWriter);
}
}
private void emptyDateWriters() {
if(!lThrdDate.isEmpty()) {
for(int i=lThrdDate.size();i>0;i--) {
ThreadStreamDateWriter threadDateWriter=lThrdDate.get(i-1);
threadDateWriter.stopThread();
lThrdDate.remove(i-1);
}
}
}
public PipedInputStream getPipedInputStream() {
return pipedInputStream;
}
public void stopThread() {
isRunning=false;
}
}
public static class ThreadStreamDateWriter extends Thread {
String Self;
private byte[] outgoingBytes;
private volatile boolean isRunning;
private /*static*/ PipedOutputStream pipedOutputStream;
ThreadStreamDateWriter(String name,PipedInputStream snk) {
Self=name;
pipedOutputStream=new PipedOutputStream();
System.out.println("ctor setting pipedOutputStream to: "+pipedOutputStream);
try {
pipedOutputStream.connect(snk);
System.out.println(pipedOutputStream+" connectd to: "+snk);
} catch(IOException e) {}
}
#Override public void run() {
isRunning=true;
while(isRunning) {
try {
outgoingBytes=getInfo().getBytes();
System.out.println("ThreadStreamDateWriter -> write to pipedOutputStream:"+new String(outgoingBytes));
System.out.println("writing to: "+pipedOutputStream);
pipedOutputStream.write(outgoingBytes);
System.out.println("ThreadStreamDateWriter -> wrote:"+new String(outgoingBytes));
try {
Thread.sleep(4000);
} catch(InterruptedException ex) {}
} catch(IOException|NegativeArraySizeException|IndexOutOfBoundsException e) {
isRunning=false;
}
}
}
String getInfo() {
String sDtTm=new SimpleDateFormat("yyyyMMdd-hhmmss").format(Calendar.getInstance().getTime());
return Self+" -> "+sDtTm;
}
public void stopThread() {
isRunning=false;
}
public String getNameDateWriter() {
return Self;
}
}
private void foo() {
if(thrdMDateReceiver==null) {
thrdMDateReceiver=new ThreadMultipleDateReceiver();
thrdMDateReceiver.start();
}
if(thrdSDateWriter0==null) {
thrdSDateWriter0=new ThreadStreamDateWriter("-0-",thrdMDateReceiver.getPipedInputStream());
thrdSDateWriter0.start();
thrdMDateReceiver.addDateWriter(thrdSDateWriter0);
}
if(thrdSDateWriter1==null) {
thrdSDateWriter1=new ThreadStreamDateWriter("-1-",thrdMDateReceiver.getPipedInputStream());
thrdSDateWriter1.start();
thrdMDateReceiver.addDateWriter(thrdSDateWriter1);
}
}
void run() throws InterruptedException {
System.out.println(("running"));
foo();
System.out.println(("sleeping"));
Thread.sleep(10000);
System.out.println(("stopping"));
if(thrdMDateReceiver!=null) {
thrdMDateReceiver.stopThread();
}
}
public static void main(String[] args) throws InterruptedException {
new So44438086().run();
}
ThreadMultipleDateReceiver thrdMDateReceiver=null;
ThreadStreamDateWriter thrdSDateWriter0=null;
ThreadStreamDateWriter thrdSDateWriter1=null;
}

Keeping and checking Process alive in Java 1.7

How to make sure process (java.lang.Process) is alive in Java 1.7. In Java 1.8, there is isAlive() method. How can it be done in Java 1.7.
Thank you!
Probably wait too late, but since I faced the same problem, here is my solution:
Just copy the implementation of the Process.isAlive() method from Java 8:
public boolean isAlive() {
try {
exitValue();
return false;
} catch(IllegalThreadStateException e) {
return true;
}
}
with out knowing about the context of need of the Process. But in general, we could use Threads with Executors framework..
Executors.newCachedThreadPool()
Then submit Tasks to it...
I used the following for monitoring multiple processes launched through a Swing application. It follows the same logic mentioned by #mastah. See whether it helps.
package snippet;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class ProcessMonitor extends Thread {
private Process process;
private int exitCode;
public ProcessMonitor(Process process) {
this.process = process;
start();
}
#Override public void run() {
try {
exitCode = process.waitFor();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void setOutputStream(final OutputStream s) {
new Thread(new Runnable() {
#Override public void run() {
InputStream is = process.getInputStream();
int c ;
try {
while((c = is.read()) >= 0) {
s.write(c);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
public void setErrorStream(final OutputStream s) {
new Thread(new Runnable() {
#Override public void run() {
InputStream is = process.getErrorStream();
int c ;
try {
while((c = is.read()) >= 0) {
s.write(c);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
public int getExitCode() {
return exitCode;
}
public static void main(String[] args) throws IOException, InterruptedException {
if(args.length == 1) {
System.err.println("In child process.. going to sleep for 1 second");
Thread.sleep(1000);
System.err.println("In child process.. done sleep exiting...");
System.exit(-1);
}
String[] pbArgs = new String[] {
"java", "-cp", System.getProperty("java.class.path"), ProcessMonitor.class.getName(), "arg"
};
ProcessBuilder pb = new ProcessBuilder(pbArgs);
pb.redirectErrorStream(true);
System.out.println("Starting process: " + pb.command());
final Process process = pb.start();
ProcessMonitor pm = new ProcessMonitor(process);
pm.setOutputStream(System.err);
while (pm.isAlive()) {
System.out.println("Process is still alive");
Thread.sleep(1000);
}
System.out.println("Process exited with: " + pm.getExitCode());
}
}

how to graceful stop java threads in sequence?

I have started threads in sequence but i don't know how to stop them in reverse sequence.
For example:
they are starting like this: A->B->C->D
and I want them to stop: D->C->B->A
I don't know how to stop threads at all and not even in this order.
I appreciate any help or advice.
import java.util.*;
class Service extends Thread
{
private RobotController controller;
private String robotID;
private byte[] lock;
public Service(RobotController cntrl, String id)
{
controller = cntrl;
robotID = id;
}
public byte[] getLock() { return lock;}
public void run()
{
lock = new byte[0];
synchronized(lock)
{
byte[] data;
while ((data = controller.getData()) == null)
{
try {
lock.wait();
} catch (InterruptedException ie) {}
}
System.out.println("Robot " + robotID + " Working");
}
}
}
class RobotController
{
private byte[] robotData;
private Vector threadList = new Vector();
private Service thread_A;
private Service thread_B;
private Service thread_C;
private Service thread_D;
private volatile boolean done;
public void setup(){
thread_A = new Service(this, "A");
thread_B = new Service(this, "B");
thread_C = new Service(this, "C");
thread_D = new Service(this, "D");
threadList.addElement(thread_A);
threadList.addElement(thread_B);
threadList.addElement(thread_C);
threadList.addElement(thread_D);
thread_A.start();
thread_B.start();
thread_C.start();
thread_D.start();
start();
stop();
}
public void start()
{
System.out.println("Thread starts");
{
for (int i=0; i <= 3; i++)
{
try {
Thread.sleep(500);
}catch (InterruptedException ie){}
putData(new byte[10]);
Service rbot = (Service)threadList.elementAt(i);
byte[] robotLock = rbot.getLock();
synchronized(robotLock) {
robotLock.notify();
}
}
}
}
public void stop()
{
{
}
}
public synchronized byte[] getData()
{
if (robotData != null)
{
byte[] d = new byte[robotData.length];
System.arraycopy(robotData, 0, d, 0, robotData.length);
robotData = null;
return d;
}
return null;
}
public void putData(byte[] d) { robotData = d;}
public static void main(String args[])
{
RobotController controller = new RobotController();
controller.setup();
}
}
I'll usually include something like a cancel() method in my threads if I want to explicitly terminate them.
class Service extends Thread {
private volatile boolean cancel = false;
public void cancel() {
cancel = true;
}
public void run() {
...
while (!cancel && (data = controller.getData()) == null) {
...
}
}
}
Keep your threads in a stack as mre suggests, then pop through the stack and call cancel and then interrupt on each thread.
I have started threads in sequence but i don't know how to stop them in reverse sequence.
This is difficult to do. There are ways you can stop a thread either by setting a volatile shutdown boolean or interrupting them, but none of these mechanisms are guaranteed to stop a thread immediately.
You certainly can keep a List<Thread> when you build them, call Collections.reverse(threadList) and then call thread.interrupt() on each one in turn. If you must have them finish in order then you should interrupt() them and then join them. Something like:
Collections.reverse(threadList);
for (Thread thread : threadList) {
thread.interrupt();
thread.join();
}
Then each thread should be doing something like:
while (!Thread.currentThread().isInterrupted()) {
...
}
Note that if you are running Thread.sleep(...) or other methods that throw InterruptedException, you'll need to re-enable the interrupt flag:
try {
Thread.sleep(...);
} catch (InterruptedException e) {
// by convention if InterruptedException thrown, interrupt flag is cleared
Thread.currentThread().interrupt();
...
}
Have each thread keep a reference to the next thread to be started. Then each thread can periodically check to see if the thread is still alive. If not, that thread should terminate. When it does, the previous thread will notice and terminate, and so on up the chain.
abstract class ChainThread extends Thread {
private final Thread next;
ChainThread(Thread next) { this.next = next; }
#Override
public final void run() {
next.start();
while (!Thread.currentThread().isInterrupted() && next.isAlive()) {
do();
}
}
abstract void do();
}
If I read the Service code correctly, it waits until there's data to execute on, then finishes. So you don't really need an explicit stop or cancel type signal, the threads will terminate themselves after they do work.
To enforce ordering of shutdown, you could make each Service aware of the previous Service, and then call previousService.join(). Assuming no InterruptedExceptions are thrown, they will then shutdown in order after seeing that the controller has data.
Create the Services this way:
Service serviceA = new Service(controller, "A", null);
Service serviceB = new Service(controller, "B", serviceA);
Service serviceC = new Service(controller, "C", serviceB);
Service serviceD = new Service(controller, "D", serviceC);
and the implementation is edited to exit only after dependent Services are complete:
private final RobotController controller;
private final String robotID;
private byte[] lock;
private final Service dependentService;
public Service(RobotController cntrl, String id, Service dependentService) {
controller = cntrl;
robotID = id;
this.dependentService = dependentService;
}
public byte[] getLock() {
return lock;
}
#Override
public void run() {
lock = new byte[0];
synchronized (lock) {
byte[] data;
while ((data = controller.getData()) == null) {
try {
lock.wait();
}
catch (InterruptedException ie) {
}
}
System.out.println("Robot " + robotID + " Working");
}
if (dependentService != null) {
try {
dependentService.join();
}
catch (InterruptedException e) {
this.interrupt();
}
}
}

Async Tcp Connection

I have a slight problem, I have a TCP class which connects to a server, transfers data then closes, this all works well except for if I connect, then stop it, it works, if I keep on doing this, it works, but on the fifth time the connection hangs without any error and doesn't transfer any data.. I've got no idea how to fix this... This is my TCP class code:
package com.millennium.isynccrm.Classes;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import android.os.AsyncTask;
import android.os.CountDownTimer;
import android.util.Log;
public class TcpClient {
public static boolean connected = false;
private static Socket socket;
private static boolean pause = false;
private static SyncClient syncClient = new SyncClient();
public static int sendCount = 0;
public static int receiveCount = 0;
private static AsyncTask<?, ?, ?> sendAsyncTask;
private static AsyncTask<?, ?, ?> receiveAsyncTask;
public TcpClient() { }
public void send (String line) {
if (!connected) connect();
while (!connected) { }
sendAsyncTask = new SenderThread().execute(line);
}
public void disconnect() {
try {
if(connected == true) {
socket.close();
connected = false;
sendCount = 0;
receiveCount = 0;
if (receiveAsyncTask.getStatus().name().equals("RUNNING")) {
receiveAsyncTask.cancel(true);
}
if (sendAsyncTask.getStatus().name().equals("RUNNING")) {
sendAsyncTask.cancel(true);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
public void pauseReceivingForTimeInterval(int milliseconds) {
pause = true;
new CountDownTimer(milliseconds, 0) {
public void onTick(long millisUntilFinished) { }
public void onFinish() {
pause = false;
}
}.start();
}
private void connect() {
new Thread(new Runnable() {
public void run() {
try {
if (connected == false) {
socket = new Socket("192.168.1.1", 80);
connected = true;
sendCount = 0;
receiveCount = 0;
try {
if (receiveAsyncTask.getStatus().name().equals("FINISHED")) {
receiveAsyncTask = new RecieverThread().execute("");
} else if (receiveAsyncTask.getStatus().name().equals("RUNNING")) {
receiveAsyncTask.cancel(true);
receiveAsyncTask = new RecieverThread().execute("");
}
} catch (Exception e) { receiveAsyncTask = new RecieverThread().execute(""); }
}
} catch (UnknownHostException e) {
connected = false;
e.printStackTrace();
} catch (IOException e) {
connected = false;
e.printStackTrace();
}
}
}).start();
}
private static PrintWriter output;
private static BufferedReader input = null;
private class RecieverThread extends AsyncTask<String, Void, String> {
protected String doInBackground(String... line) {
while (pause) { }
try {
receiveCount++;
if (receiveCount == 1) input = new BufferedReader(new InputStreamReader(socket.getInputStream()), 8 * 1024);
return input.readLine();
} catch (IOException e) {
Log.d("error", "stop");
}
return "stop";
}
protected void onPostExecute(String result) {
if (result == null) return;
else if (!result.equals("") && !result.equals("stop")) syncClient.recieveMessage(result);
else if (!result.equals("stop")) receiveAsyncTask = new RecieverThread().execute("");
}
}
private class SenderThread extends AsyncTask<String, Void, String> {
protected String doInBackground(String... line) {
while (pause) { }
sendCount++;
if (sendCount == 1) {
try {
output = new PrintWriter(socket.getOutputStream(),true);
} catch(Exception e) { e.printStackTrace(); }
}
String msg = line[0] + "\r\n";
output.print(msg);
output.flush();
return "";
}
protected void onPostExecute(String result) {
}
}
}
As you can see another class sends a line to this class to send to the server, then a Async task sends it and another Async task receives it and passes it onto another class which processes it and so on. On the UI I have a start button which when is pressed turns into a stop button and when stop is pressed it calls the 'disconnect' function in here.
I'm not too sure wherever I have done my RecieverThread AsyncTask is correct, it waits for a line from the socket then passes it on and restarts it self to listen for another line, is this a 'good' way of doing this? Or is it a terrible way (which I imagine it is). To be honest I think this class is very 'messy' and I will more than likely be redoing it.
Any suggestion why I can never send data on the fifth time I connect to the server? (One last note, the server is not to blame here, as we have a iPhone app which does the same thing) (Extra side note.. I'm pretty new to Tcp connections and that sort of stuff, and new to Threading/Async Tasks.) :)
Any help, assistance would be much appreciated (: Thanks!
while (!connected) {}??? that's no way to do asynchronous anything. You are hogging the CPU waiting for something to happen, and preventing it from happening by hogging the CPU. Use a Selector, or do a blocking-mode connect.

how to restart a thread

I tried to write a file monitor which will check the file if a new line is appended,the monitor in fact is a thread which will read the line by a randomaccessfile all the time.
This is the monitor core codes:
public class Monitor {
public static Logger log = Logger.getLogger(Monitor.class);
public static final Monitor instance = new Monitor();
private static final ArrayList<Listener> registers = new ArrayList<Listener>();
private Runnable task = new MonitorTask();
private Thread monitorThread = new Thread(task);
private boolean beStart = true;
private static RandomAccessFile raf = null;
private File monitoredFile = null;
private long lastPos;
public void register(File f, Listener listener) {
this.monitoredFile = f;
registers.add(listener);
monitorThread.start();
}
public void replaceFile(File newFileToBeMonitored) {
this.monitoredFile = newFileToBeMonitored;
// here,how to restart the monitorThread?
}
private void setRandomFile() {
if (!monitoredFile.exists()) {
log.warn("File [" + monitoredFile.getAbsolutePath()
+ "] not exist,will try again after 30 seconds");
try {
Thread.sleep(30 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
setRandomFile();
return;
}
try {
if (raf != null) {
raf.close();
lastPos = 0;
}
raf = new RandomAccessFile(monitoredFile, "r");
log.info("monitor file " + monitoredFile.getAbsolutePath());
} catch (FileNotFoundException e) {
// The file must exist now
} catch (IOException e) {}
}
private void startRead() {
beStart = true;
String line;
while (beStart) {
try {
raf.seek(lastPos);
while ((line = raf.readLine()) != null) {
fireEvent(new FileEvent(monitoredFile.getAbsolutePath(),
line));
}
lastPos = raf.getFilePointer();
} catch (IOException e1) {}
}
}
private void stopRead() {
this.beStart = false;
}
private void fireEvent(FileEvent event) {
for (Listener lis : registers) {
lis.lineAppended(event);
}
}
private class MonitorTask implements Runnable {
#Override
public void run() {
stopRead();
//why putting the resetReandomAccessFile in this thread method is that it will sleep if the file not exist.
setRandomFile();
startRead();
}
}
}
This is some help classes:
public interface Listener {
void lineAppended(FileEvent event);
}
public class FileEvent {
private String line;
private String source;
public FileEvent(String filepath, String addedLine) {
this.line = addedLine;
this.source = filepath;
}
//getter and setter
}
And this is a example to call the monitor:
public class Client implements Listener {
private static File f = new File("D:/ab.txt");
public static void main(String[] args) {
Monitor.instance.register(f, new Client());
System.out.println(" I am done in the main method");
try {
Thread.sleep(5000);
Monitor.instance.replaceFile(new File("D:/new.txt"));
} catch (InterruptedException e) {
System.out.println(e.getMessage());
}
}
#Override
public void lineAppended(FileEvent event) {
String line = event.getLine();
if (line.length() <= 0)
return;
System.err.println("found in listener:" + line + ":" + line.length());
}
}
Now,my probelm is the code work well if I just call:
Monitor.instance.register(file,listener);
This will monitor the file for line appending,and will notify the listener.
However it does not work when I call the :
Monitor.instance.replaceFile(anotherfile);
This means I want to monitor another file rather than before.
So in my Monitor I have to restart the thread,how to make it?
I have tried the:
monitorThread.interruppt();
It does not wrok.
Anyone can fix it for me or tell me how to do ?
Thanks.
Before I ask,I have googling the "restart java thread",so I know one can not restart a dead thread,but my thread does not return,so I think it can be restarted.
You don't restart a Thread, instead you create a new one each time you want to start a thread.
A better alternative may be to use Executors.newCachedThreadPool() which gives you a pool of thread which will be started/recycle for you.
BTW: You are using recursion rather than a loop to poll if the file exists. Using recursion can mean if you wait too long it will throw a StackOverflowError. IMHO you shouldn't wait at all, the polling thread should repeatedly attempt to open the file until it is told to stop (or the file appears)
Your current implementation also means if the file is replaced, you will have to reopen the file in the background thread anyway.
Instead of explaining, I just coded up a skeleton example. I did not test it terribly well, but it may be of some use.
In order to monitor a(nother) file, just create a new Monitor, passing it a ScheduledExecutorService. Starting and stopping monitoring is straightforward. You can (should) reuse the same executor for multiple monitors.
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public interface Event
{
}
public interface Listener
{
void handle(Event event);
}
public class Monitor
{
private static final int CHECK_EVERY_SECONDS = 10;
private static final int RECHECK_AFTER_IF_NOT_EXISTS_SECONDS = 30;
private File file;
private ScheduledExecutorService executor;
private boolean active;
private List<Listener> listeners;
public Monitor(File file, ScheduledExecutorService executor)
{
super();
this.file = file;
this.executor = executor;
listeners = new ArrayList<Listener>();
}
public synchronized void start()
{
if (active)
{
return;
}
active = true;
executor.execute(new Runnable()
{
public void run()
{
synchronized (Monitor.this)
{
if (!active)
{
System.out.println("not active");
return;
}
}
if (!file.exists())
{
System.out.println("does not exist, rescheduled");
executor.schedule(this, RECHECK_AFTER_IF_NOT_EXISTS_SECONDS, TimeUnit.SECONDS);
return;
}
Event event = doStuff(file);
System.out.println("generated " + event);
updateListeners(event);
System.out.println("updated listeners and rescheduled");
executor.schedule(this, CHECK_EVERY_SECONDS, TimeUnit.SECONDS);
}
});
}
private Event doStuff(final File file)
{
return new Event()
{
public String toString()
{
return "event for " + file;
}
};
}
public synchronized void stop()
{
active = false;
}
public void addListener(Listener listener)
{
synchronized (listeners)
{
listeners.add(listener);
}
}
public void removeListener(Listener listener)
{
synchronized (listeners)
{
listeners.remove(listener);
}
}
private void updateListeners(Event event)
{
synchronized (listeners)
{
for (Listener listener : listeners)
{
listener.handle(event);
}
}
}
public static void main(String[] args) throws IOException
{
ScheduledExecutorService executor = Executors.newScheduledThreadPool(4);
File file = new File("test.png");
Monitor monitor = new Monitor(file, executor);
monitor.addListener(new Listener()
{
public void handle(Event event)
{
System.out.println("handling " + event);
}
});
monitor.start();
System.out.println("started...");
System.in.read();
monitor.stop();
System.out.println("done");
executor.shutdown();
}
}
See this post How to start/stop/restart a thread in Java?
I assume you answered your question
one can not restart a dead thread
This link may be helpful to you How to restart thread in java?
A thread in Java cannot be re-started. Every time you need to restart the thread you must make a new one.
That said, you might want to look at:
private void setRandomFile() {
if (!monitoredFile.exists()) {
log.warn("File [" + monitoredFile.getAbsolutePath()
+ "] not exist,will try again after 30 seconds");
try {
Thread.sleep(30 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
setRandomFile();
return;
}
// ....
}
Here you sleep for 30 seconds if the file does not exist, then recursively call the same function. Now, I don't know what business requirements you have, but if this recursion ran long enough you will run out of stack space. Perhaps you will be better served with a while loop or even better, a little synchronisation like a Semaphore.

Categories

Resources