How can I interrupt a sequence of procedures in Java?
-
19-09-2019 - |
Question
I have a bunch of procedures that need to be executed successively until either they are all executed, or a certain condition is met. Here's the basic code that needs to be executed until a condition is met:
public boolean search()
{
robot.go();
robot.spin();
//etc - around 8 more similar commands (each takes around 2 seconds)
return false; //didn't find what it was looking for
}
So far, the only way that I've thought of doing what I wanted is this:
public boolean search()
{
robot.go(false);
while(robot.isMoving())
{
if(thingFound())
{
robot.stop()
return true;
}
Thread.yield();
}
robot.spin(false);
while(robot.isMoving())
{
if(thingFound())
{
robot.stop()
return true;
}
Thread.yield();
}
//etc - around 8 more similar commands
return false; //didn't find what it was looking for
}
The false
parameter to go()
and spin()
indicates that they should return immediately, allowing the condition to be checked. However, this approach strikes me as rather inefficient, as the same block of code must be repeated 10 times. Could this be achieved more efficiently with exceptions or concurrent Threads?
Solution
based on Jonathan Leffler's answer:
you can use a Runnable as pointer to the commands
private final Runnable going = new Runnable() {
@Override
public void run() {
robot.go(false);
}
});
private final Runnable spinning = new Runnable {
@Override
public void run() {
robot.spin(false);
}
});
// other commands
private boolean isFoundAfter(Runnable command)
{
command.run();
while (robot.isMoving())
{
if (thingFound())
{
robot.stop()
return true;
}
Thread.yield();
}
return false;
}
public boolean search()
{
if (isFoundAfter(going)) return true;
if (isFoundAfter(spinning)) return true;
//etc - around 8 more similar commands
return false; //didn't find what it was looking for
}
one further step, if appropriate, put the commands in an array or a List and execute it as a script
...
private boolean executeSearch(Runnable... commands)
{
for (Runnable cmd : commands) {
if (isFoundAfter(cmd)) return true;
}
return false; //didn't find what it was looking for
}
public boolean search() {
return executeSearch(going, spinning /* around 8 more similar commands */);
}
OTHER TIPS
Not sure why you are using Thread.yield()
- are there other threads executing that you didn't mention? Or maybe I misread the problem.
I think maybe the Command pattern could work here. You would have a RobotCommand
interface with an execute
method, and an implementation of RobotCommand
per command type (go, spin, etc). Then you could construct a RobotAlgorithm
as a List
of RobotCommand
, and have a method executeRobotAlgorithm
that iterated over the list, calling execute
on each RobotCommand
and checking the result of thingFound()
after each one.
Edit - oh, I think I get it. Do go
and spin
kick off threads that change the state of the robot, or something like that?
Edit 2 - in response to your comment, it sounds like the problem here is that you need to be able to return immediately if the robot finds what it's looking for, but the go
, spin
, etc commands won't do this right now, and you need the ability to keep executing new commands in the meantime. So what I might do here is have two threads - one would be an "executor" thread that would execute your List
of RobotCommand
s one by one, and a "watcher" thread that will repeatedly sleep and poll (check thingFound()
). If thingFound()
is ever true then you can stop your robot as well as the executor thread, or if the executor gets to the end before thingFound()
is true then it can signal as such (if necessary).
Clearly, the while loop can be packaged into its own function:
private boolean isFound()
{
while (robot.isMoving())
{
if (thingFound())
{
robot.stop()
return true;
}
Thread.yield();
}
return false;
}
public boolean search()
{
robot.go(false);
if (isFound()) return true;
robot.spin(false);
if (isFound()) return true;
//etc - around 8 more similar commands
return false; //didn't find what it was looking for
}
(I don't mind if the conditional is split over two lines; I'd probably do that in production code.)
A better Java programmer than I can tell you whether you can pass 'procedures' around (pointers to functions, in terms of C programming). I suspect you can, but I don't know the syntax and rules. The evidence seems to be that you can't (circa 2004, anyway).
robot can use a Condition object to signal to the controller that it's completed some sub-task or entered a new state:
http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/locks/Condition.html
interesting, for the environment that's in you could probably use and notify() and wait() instead of the more flexible Condition. controller could wait() until the robot decides to release him with notify().