2012-07-10 79 views
1

我在java程序中创建了三个线程。一个是主程序,另外一个是扩展Thread的两个类。主线程表示机器的控制器。另一个线程是执行器,第三个是传感器。控制器设置由执行器线程读取的自己类别的变量。执行器根据指令执行某些动作并更新自己的内部变量。这些读数据由读取执行器变量(表示真实世界行为)的传感器线程读取,并设置其自己的内部变量,然后由控制器读取并且我们已经走完了整圈。然后控制器根据新的感测世界设置变量等。线程缺少其他线程的更新

执行器处于一个永久循环中,每个循环中睡眠100 ms。 传感器也处于永久循环中,每个循环睡眠20ms。

该系统几乎可以工作。主循环会遗漏来自传感器的更新,除非我为其添加睡眠。我的问题是现在这是为什么?即使睡眠(0)也会使系统正常工作。我已经将语句放在performJob(作业输入)while循环中。没有睡眠调用的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; 
    } 
} 

现实是静态构成的一类领域和会见由执行器写入并由传感器读取。

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()调用替换睡眠呼叫,该程序将工作。

回答

1

这里发生的事情最有可能是内存一致性错误。当控制器类设置内部控制变量,然后进入等待传感器的循环时,它很可能会阻止执行器和传感器类正确更新对控制器看到的读数,并因此阻止控制器看到正确的值。通过将同步语句添加到从另一个类读取的所有函数中,问题就解决了。我只能推测,睡眠调用让控制器线程输入了某种让其他线程对变量的更改可见的同步块。

0

您正在使用哪个JVM?

作为快速解决方法,您可以为线程之间共享的字段设置易失性。

也请期待演员的方式为消息:http://doc.akka.io/docs/akka/snapshot/java/untyped-actors.html

+0

这是正在使用的jvm - 版本:1.6.0_16 名称:Java HotSpot(TM)64位服务器虚拟机14.2-b01 – Steinin 2012-07-10 04:51:07

+0

我建议您尝试Java 6更新33,因为更新16大约两岁。 – 2012-07-10 05:19:33

+0

可悲的是,我没有合适的用户权限来更新或安装大学计算机上的软件。 – Steinin 2012-07-10 22:03:30

1

谷歌的‘生产者消费者队列’。

除非需要延迟,效率低下和丢失数据,否则请不要将sleep()用于线程间通信。有更好的机制可以避免睡眠(),并试图直接从某个共享/锁定对象读取有效数据。

如果使用commands/requests/data加载'comms'对象,将它们排队等待其他线程并立即为后续通信创建另一个comms对象,则线程间通信将快速安全,不会sleep()并且没有任何线程读取陈旧的数据或被另一个线程更改的机会。

+0

在阅读有关队列时更新了我的问题。 – Steinin 2012-07-10 22:04:33