Безусловно, лучший механизм для прикрепления потоков к чему-либо - это отделение объекта, который выполняет работу, от объекта, который является потоком. Интерфейс Runnable может быть привлекательным, поскольку он позволяет людям передавать объект в конструктор Thread или Executor и запускать его. Однако, если у вас есть требования к управлению жизненным циклом для вашего объекта, которые выходят за рамки «запуска до завершения», например, приостановки, то в большинстве случаев вы найдете более подходящим управление потоком внутри вашего объекта, чтобы вы знали, какой поток работает (да, вы можете установить для объекта экземпляра Thread.currentThread () в записи для запуска, но ...).
Итак, я думаю, что у вас есть хорошее начало. Вам нужно добавить использование некоторой блокировки, чтобы помочь себе управлять pause () и другими средствами управления потоками.
public class PIDController {
private final Object myLock = new Object();
private final ControllerMethods ctrl;
private volatile Thread thread;
private volatile Runner runner;
private int pidInterval = 700;
private final double setPoint, kp, ki, kd;
public PIDController( double setPoint, double kp, double ki, double kd, ControllerMethods inst ) {
this.ctrl = inst;
this.setPoint = setPoint;
this.kp = kp;
this.ki = ki;
this.kd = kd;
}
public void pause() {
synchronized( myLock ) {
if( runner.paused ) {
throw new IllegalOperationException(this+": already paused");
}
runner.paused = true;
}
}
public void resume() {
synchronized( myLock ) {
if( !runner.paused ) {
throw new IllegalOperationException(this+": already resumed");
}
runner.paused = false;
}
}
public bool isRunning() {
return running;
}
public void start() {
if( thread != null ) {
throw new IllegalOperationException( this+": already running");
}
myThread = new Thread( runner = new Runner() );
myThread.start();
}
public void stop() {
if( runner == null ) {
throw new IllegalOperationException( this+": PID is not running");
}
runner.running = false;
if( runner.paused )
resume();
runner = null;
}
// It is important, anytime that you implement a stoppable Runnable, that
// you include the "running" flag as a member of an innner instance class like
// this so that when you ask this instance to stop, you can immediately restart
// another instance and not have the two threads observing the same "running" flag
private class Runner implements Runnable {
volatile bool running = false, bool paused;
public void run() {
running = true;
while( running ) {
// do this at the top of the loop so that a wake from
// pause will check running, before recomputing.
reComputePID();
// Use the double check idiom to
if( paused ) {
synchronized( myLock ) {
while( paused ) {
myLock.wait();
}
}
}
Thread.sleep( pidInterval );
}
}
}
public void reComputePID() {
...
}
}