我在一个 java 程序中创建了三个线程。一个是主程序,另外两个是扩展 Thread 的两个类。主线程代表机器的控制器。另一个线程是执行器,第三个是传感器。控制器在其自己的类中设置变量,由执行器线程读取。执行器根据指令执行某些动作并更新自己的内部变量。这些依次由传感器线程读取,该线程读取执行器变量(代表现实世界的动作)并设置其自己的内部变量,这些变量又由控制器读取,我们已经完成了一个完整的循环。然后控制器根据新感知的世界等设置变量。
执行器处于一个永久循环中,每个循环中休眠 100 毫秒。传感器也处于一个永久循环中,每个循环休眠 20 毫秒。
该系统几乎可以工作。主循环将错过传感器的更新,除非我也为其添加睡眠。我现在的问题是为什么会这样?即使 sleep(0) 也会使系统正常工作。我已将语句放在 performJob(Job input) while 循环中。没有 sleep 调用的 java 主线程的行为与具有相同线程的线程有何不同?并发对我来说是一个相当新的主题。
这是我正在使用的代码:
主要的:
public class Main {
public static void main(String[] args) {
Controller Press = new Controller();
Press.processUnits(1); // no reset code at the moment, only use 1 at a time
Press.shutdownThreads();
}
}
控制器:
import java.util.LinkedList;
public class Controller extends Thread {
// Constants
static final int STATE_WAITING = 0;
static final int STATE_MOVE_ARMS = 1;
static final int STATE_MOVE_PRESS = 2;
static final int LOADINGARM = 2;
static final int UNLOADINGARM = 1;
static final int NOARM = 0;
static final boolean EXTEND = true;
static final boolean RETRACT = false;
private enum Jobs {
EXTENDRETRACT, ROTATE, MAGNETONOFF, PRESSLOWERRAISE
}
// Class variables
private int currentState;
// Component instructions
private int armChoice = 0;
private boolean bool = false; // on, up, extend / off, down, retract
private boolean[] rotInstr = {false, false}; // {rotate?, left true/right false}
private boolean errorHasOccurred = false;
private boolean pressDir = false;
private Sensors sense = null;
private Actuators act = null;
private LinkedList<Job> queue = null;
// Constructor
public Controller() {
act = new Actuators(0.0f, this);
sense = new Sensors();
act.start();
sense.start();
currentState = STATE_WAITING;
queue = new LinkedList<Job>();
}
// Methods
int[] getArmInstructions() {
int extret = (bool) ? 1 : 0;
int[] ret = {armChoice, extret};
return ret;
}
boolean[] getRotation() {
return rotInstr;
}
int getControllerState() {
return currentState;
}
boolean getPressDirection() {
return pressDir;
}
public boolean processUnits(int nAmount) {
if (run(nAmount)) {
System.out.println("Controller: All units have been processed successfully.");
return true;
} else { // procUnits returning false means something went wrong
System.out.println("Controller: An error has occured. The process cannot complete.");
return false;
}
}
/*
* This is the main run routine. Here the controller analyses its internal state and its sensors
* to determine what is happening. To control the arms and press, it sets variables, these symbolize
* the instructions that are sent to the actuators. The actuators run in a separate thread which constantly
* reads instructions from the controller and act accordingly. The sensors and actuators are dumb, they
* will only do what they are told, and if they malfunction it is up to the controller to detect dangers or
* malfunctions and either abort or correct.
*/
private boolean run(int nUnits) {
/*
Here depending on internal state, different actions will take place. The process uses a queue of jobs
to keep track of what to do, it reads a job, sets the variables and then waits until that job has finished
according to the sensors, it will listen to all the sensors to make sure the correct sequence of events is
taking place. The controller reads the sensor information from the sensor thread which runs on its own
similar to the controller.
In state waiting the controller is waiting for input, when given the thread cues up the appropriate sequence of jobs
and changes its internal state to state move arms
In Move arms, the controller is actively updating its variables as its jobs are performed,
In this state the jobs are, extend, retract and rotate, pickup and drop.
From this state it is possible to go to move press state once certain conditions apply
In Move Press state, the controller through its variables control the press, the arms must be out of the way!
In this state the jobs are press goes up or down. Pressing is taking place at the topmost position, middle position is for drop off
and lower is for pickup. When the press is considered done, the state reverts to move arms,
*/
//PSEUDO CODE:
//This routine being called means that there are units to be processed
//Make sure the controller is not in a blocking state, that is, it shut down previously due to errors
//dangers or malfunctions
//If all ok - ASSUMED SO AS COMPONENTS ARE FAULT FREE IN THIS VERSION
if (!errorHasOccurred) {
//retract arms
currentState = STATE_MOVE_ARMS;
queue.add(new Job(Jobs.EXTENDRETRACT, LOADINGARM, RETRACT));
queue.add(new Job(Jobs.EXTENDRETRACT, UNLOADINGARM, RETRACT));
performWork();
//System.out.println("Jobs added");
//while there are still units to process
for (;nUnits != 0; nUnits--) {
//move the press to lower position, for unloading
currentState = STATE_MOVE_PRESS;
//rotate to pickup area and pickup the metal, also pickup processed
currentState = STATE_MOVE_ARMS;
queue.add(new Job(Jobs.EXTENDRETRACT, LOADINGARM, EXTEND));
queue.add(new Job(Jobs.EXTENDRETRACT, UNLOADINGARM, EXTEND));
performWork();
//retract and rotate
queue.add(new Job(Jobs.EXTENDRETRACT, LOADINGARM, RETRACT));
queue.add(new Job(Jobs.EXTENDRETRACT, UNLOADINGARM, RETRACT));
performWork();
//state change, press moves to middle position
currentState = STATE_MOVE_PRESS;
//state change back, put the metal on the press, drop processed and pull arms back
currentState = STATE_MOVE_ARMS;
queue.add(new Job(Jobs.EXTENDRETRACT, LOADINGARM, EXTEND));
queue.add(new Job(Jobs.EXTENDRETRACT, UNLOADINGARM, EXTEND));
queue.add(new Job(Jobs.EXTENDRETRACT, LOADINGARM, RETRACT));
queue.add(new Job(Jobs.EXTENDRETRACT, UNLOADINGARM, RETRACT));
performWork();
//state change, press the metal in upper position
currentState = STATE_MOVE_PRESS;
//repeat until done
}
//unload final piece
//move the press to lower position for unload
//rotate and pickup processed piece
//drop it off at unloading and wait for more orders
currentState = STATE_WAITING;
}
return true;
}
private boolean performWork() {
while (!queue.isEmpty()) {
performJob(queue.removeFirst());
}
return true;
}
private boolean performJob(Job input) {
//The purpose of this function is to update the variables and wait until they are completed
// read in the job and check appropriate sensors
boolean[] data = sense.getSensorData(); // LExt, LRet, UlExt, UlRet
printBools(data);
int Instruction = input.Instruction;
boolean skipVars = false;
if (input.Job == Jobs.EXTENDRETRACT) {
if (currentState != STATE_MOVE_ARMS){
System.out.println("Wrong state in performJob. State is "+currentState+" expected "+STATE_MOVE_ARMS);
return false;
}
if ((Instruction == LOADINGARM) && (input.Bool)) skipVars = data[0];
if ((Instruction == LOADINGARM) && (!input.Bool)) skipVars = data[1];
if ((Instruction == UNLOADINGARM) && (input.Bool)) skipVars = data[2];
if ((Instruction == UNLOADINGARM) && (!input.Bool)) skipVars = data[3];
}
if (!skipVars) {
// if sensors not at intended values, update correct variables
System.out.println("Controller: Did not skip vars");
switch (input.Job) {
case EXTENDRETRACT:
armChoice = (Instruction == LOADINGARM) ? LOADINGARM : UNLOADINGARM;
bool = input.Bool;
break;
case ROTATE:
break;
case MAGNETONOFF:
break;
case PRESSLOWERRAISE:
break;
default:
System.out.println("Default called in performJob()");
break;
}
}
// listen to sensors until completed
boolean done = false;
System.out.println("Waiting for sensor data.");
//System.out.print("Instruction is "+Instruction+" and bool is "); if (input.Bool) System.out.print("true\n"); else System.out.print("false\n");
while (!done) {
data = sense.getSensorData();
// REMOVING THIS TRY STATEMENT BREAKS THE PROGRAM
try {
Thread.currentThread().sleep(10);
} catch (InterruptedException e) {
System.out.println("Main thread couldn't sleep.");
}
// Check appropriate sensors
if (input.Job == Jobs.EXTENDRETRACT) {
if ((Instruction == LOADINGARM) && (input.Bool)) done = data[0];
if ((Instruction == LOADINGARM) && (!input.Bool)) done = data[1];
if ((Instruction == UNLOADINGARM) && (input.Bool)) done = data[2];
if ((Instruction == UNLOADINGARM) && (!input.Bool)) done = data[3];
}
}
// reset all variables
armChoice = 0;
bool = false;
// when done return
System.out.println("Finished "+input.Job);
return true;
}
public void shutdownThreads() {
sense.shutDown();
act.shutDown();
}
private class Job {
// Class variables
Jobs Job;
int Instruction;
boolean Bool; // used for directions, up/down, left/right, extend/retract
// Constructor
Job(Jobs newJob, int newInstruction, boolean dir) {
Job = newJob;
Instruction = newInstruction;
Bool = dir;
}
}
private void printBools(boolean[] input) {
System.out.println();
for (int i = 0; i < input.length; i++) {
if (input[i]) System.out.print("true "); else System.out.print("false ");
}
System.out.println();
}
}
执行器:
public class Actuators extends Thread {
// Constants
private final int ARM = 0, ROTATE = 0; // array indexes - which arm, rotate yes/no?
private final int DIR = 1, ROTDIR = 1; // array indexes - which direction ext/ret, rotate direction
private final int EXT = 1;//, RET = 0;
private final double ARM_SPEED = 5.0;
// Class variables
private Controller Owner = null;
private boolean run = true;
// Constructor
Actuators(float nPos, Controller Owner) {
Reality.changeAngle(nPos);
this.Owner = Owner;
}
// Methods
private void rotate(boolean dir) {
float nAngle = dir ? 0.1f : -0.1f;
Reality.changeAngle(nAngle);
}
public void run() {
while (run) {
try {
sleep(100);
} catch (InterruptedException e) {
System.out.println("Actuators couldn't sleep");
}
// read variables in controller
int nState = Owner.getControllerState();
if (nState == Controller.STATE_MOVE_ARMS) {
boolean[] rot = Owner.getRotation();
if (rot[ROTATE]) { // Rotation?
rotate(rot[ROTDIR]);
} else { // or arm extensions
int[] instr = Owner.getArmInstructions();
if (instr[ARM] != Controller.NOARM) { // 0 = no arm movement
//System.out.println("Actuator arm is "+instr[ARM]);
double dir = (instr[DIR] == EXT) ? ARM_SPEED : -ARM_SPEED; // 1 = extend, 0 = retract
Reality.changeArmLength(instr[ARM], dir);
}
}
}
}
}
void shutDown() {
run = false;
}
}
Reality 是一个由静态字段和方法组成的类,由执行器写入并由传感器读取。
public class Reality {
// Constants
static private final double EXTEND_LIMIT = 100.0;
static private final double RETRACT_LIMIT = 0.0;
// Variables
private static float ArmsAngle = 0.0f;
// Read by Sensor
static double LoadingArmPos = 0.0;
static double UnloadingArmPos = 0.0;
// Methods
static void changeAngle(float newAngle) {
ArmsAngle = ArmsAngle + newAngle;
if ((ArmsAngle < 0.0f) || (ArmsAngle > 90.0f))
System.out.println("Reality: Unallowed Angle");
}
static void changeArmLength(int nArm, double dPos) { // true = extend, false = retract
switch (nArm) {
case Controller.LOADINGARM:
LoadingArmPos += dPos;
checkArmPos(LoadingArmPos);
break;
case Controller.UNLOADINGARM:
UnloadingArmPos += dPos;
checkArmPos(UnloadingArmPos);
break;
default:
System.out.println("Arm other than 2 (load) or 1 (unload) in changeArmLength in Reality");
break;
}
}
static float senseAngle() {
return ArmsAngle;
}
static private boolean checkArmPos(double dPos) {
// Allowed positions are 100.0 to 0.0
if ((dPos > EXTEND_LIMIT) || (dPos < RETRACT_LIMIT)) {
System.out.println("Arm position impossible in reality. Is "+dPos);
return true;
} else {
return false;
}
}
}
最后是传感器:
public class Sensors extends Thread {
// Constants
private final double EXTENDED = 100.0;
private final double RETRACTED = 0.0;
private final double MARGIN = 0.1;
// Class Variables
private boolean run = true;
// Read by Controller
private boolean LoadingExtended = true;
private boolean LoadingRetracted = true;
private boolean UnloadingExtended = true;
private boolean UnloadingRetracted = true;
// Constructor
Sensors() {
LoadingExtended = false;
LoadingRetracted = true;
UnloadingExtended = false;
UnloadingRetracted = true;
}
// Methods
boolean senseLoadingExtended() {
return (Math.abs(Reality.LoadingArmPos - EXTENDED) < MARGIN);
}
boolean senseLoadingRetracted() {
return (Math.abs(Reality.LoadingArmPos - RETRACTED) < MARGIN);
}
boolean senseUnloadingExtended() {
return (Math.abs(Reality.UnloadingArmPos - EXTENDED) < MARGIN);
}
boolean senseUnloadingRetracted() {
return (Math.abs(Reality.UnloadingArmPos - RETRACTED) < MARGIN);
}
// called by Controller
boolean[] getSensorData() {
boolean[] ret = {LoadingExtended, LoadingRetracted, UnloadingExtended, UnloadingRetracted};
return ret;
}
// Sensor primary loop
public void run() {
while (run) {
try {
sleep(20);
}
catch (InterruptedException e) {
System.out.println("Sensors couldn't sleep");
}
LoadingExtended = senseLoadingExtended();
LoadingRetracted = senseLoadingRetracted();
UnloadingExtended = senseUnloadingExtended();
UnloadingRetracted = senseUnloadingRetracted();
}
}
void shutDown() {
run = false;
}
}
此版本中并未读取所有字段和函数。该程序是对以前主要使用函数调用的单线程应用程序的改造。为了便于阅读,我对代码进行了一些清理。即使不是最初的问题,也欢迎建设性的设计评论。发生了一些非常可疑的事情。我通常不是一个迷信的编码员,但我可以例如用 System.out.println() 调用替换睡眠调用,并且程序将运行。