简体   繁体   English

java中的高效状态机模式

[英]Efficient state machine pattern in java

I am writing a java simulation application which has a lot of entities to simulate. 我正在编写一个java模拟应用程序,它有很多实体可供模拟。 Each of these entities has a certain state at any time in the system. 这些实体中的每一个在系统中的任何时间都具有某种状态。 A possible and natural approach to model such an entity would be using the state (or state machine) pattern. 对这样的实体进行建模的可能且自然的方法是使用状态(或状态机)模式。 The problem is that it creates a lot of objects during the runtime if there are a lot of state switches, what might cause bad system performance. 问题是如果有很多状态切换,它会在运行时创建很多对象,这可能会导致系统性能下降。 What design alternatives do I have? 我有哪些设计选择? I want performance to be the main criteria after maintainability. 我希望性能成为可维护性之后的主要标准。

Thanks 谢谢

The below code will give you high performance (~10ns/event) zero runtime GC state machine implementation. 以下代码将为您提供高性能(~10ns / event)零运行时GC状态机实现。 Use explicit state machines whenever you have a concept of state in the system or component, this not only makes the code clean and scalable but also lets people (not even programmers) see immediately what the system does without having to dig in numerous callbacks: 只要系统或组件中有状态概念,就可以使用显式状态机,这不仅可以使代码保持清洁和可扩展,还可以让人们(甚至程序员)立即看到系统的功能,而无需深入研究多个回调:

abstract class Machine {
    enum State {
      ERROR,
      INITIAL,
      STATE_0,
      STATE_1,
      STATE_2;
    }

    enum Event {
      EVENT_0,
      EVENT_1,
      EVENT_2;
    }

    public static final int[][] fsm;
    static {
      fsm = new int[State.values().length][];
      for (State s: State.values()) {
        fsm[s.ordinal()] = new int[Event.values().length];
      }
    }

    protected State state = State.INITIAL;
    // child class constructor example
    // public Machine() {
    //   // specify allowed transitions
    //   fsm[State.INITIAL.ordinal()][Event.EVENT_0.ordinal()] = State.STATE_0.ordinal();
    //   fsm[State.STATE_0.ordinal()][Event.EVENT_0.ordinal()] = State.STATE_0.ordinal();
    //   fsm[State.STATE_0.ordinal()][Event.EVENT_1.ordinal()] = State.STATE_1.ordinal();
    //   fsm[State.STATE_1.ordinal()][Event.EVENT_1.ordinal()] = State.STATE_1.ordinal();
    //   fsm[State.STATE_1.ordinal()][Event.EVENT_2.ordinal()] = State.STATE_2.ordinal();
    //   fsm[State.STATE_1.ordinal()][Event.EVENT_0.ordinal()] = State.STATE_0.ordinal();
    //   fsm[State.STATE_2.ordinal()][Event.EVENT_2.ordinal()] = State.STATE_2.ordinal();
    //   fsm[State.STATE_2.ordinal()][Event.EVENT_1.ordinal()] = State.STATE_1.ordinal();
    //   fsm[State.STATE_2.ordinal()][Event.EVENT_0.ordinal()] = State.STATE_0.ordinal();
    // }

    public final void onEvent(Event event) {
      final State next = State.values()[ fsm[state.ordinal()][event.ordinal()] ];
      if (next ==  State.ERROR) throw new RuntimeException("invalid state transition");
      if (acceptEvent(event)) {
        final State prev = state;
        state = next;
        handleEvent(prev, event);
      }
    }

    public abstract boolean acceptEvent(Event event);
    public abstract void handleEvent(State prev, Event event);
}

if fsm is replaced with a unidimentional array of size S*E it will also improve cache proximity characteristics of the state machine. 如果用大小为S * E的单定义数组替换fsm,它还将改善状态机的高速缓存接近特性。

My suggestion: 我的建议:
Have you "transitions managment" be configurable (ie - via XML). 您是否可以配置“转换管理”(即 - 通过XML)。

Load the XML to a repository holding the states. 将XML加载到包含状态的存储库。
The internal data structure will be a Map: 内部数据结构将是Map:

Map<String,Map<String,Pair<String,StateChangeHandler>>> transitions;

The reason for my selection is that this will be a map from a state name 我选择的原因是这将是来自州名的地图
To a map of "inputs" and new states: 到“输入”和新状态的地图:
Each map defines a map between possible input and the new state it leads to which is defined by the state name and a StateChangeHandler I will elaborate on later 每个映射都定义了一个可能的输入和它所导致的新状态之间的映射,该状态由状态名称和一个StateChangeHandler定义,我将在后面详细说明
change state method at the repository would have a signature of: 存储库中的更改状态方法将具有以下签名:

void changeState(StateOwner owner, String input)

This way the repository is stateless in the sense of the state owner using it, you can copy one copy, 这样,在使用它的状态所有者的意义上,存储库是无状态的,你可以复制一个副本,
and not worry about thread safety issues. 而不用担心线程安全问题。
StateOwner will be an interface your Classes that need state changing should implement. StateOwner将是您需要进行状态更改的类应该实现的接口。
I think the interface should look like this: 我认为界面应如下所示:

public interace StateOwner {
   String getState();
   void String setState(String newState);
}

In addition, you will have a ChangeStateHandler interface: 此外,您将拥有ChangeStateHandler接口:

public interface StateChangeHandler {
    void onChangeState(StateOwner, String newState) {
    }
}

When the repository's changeState method is called, it will 当调用存储库的changeState方法时,它会
check at the data structure that the current state of the stateOwner has a map of "inputs". 在数据结构中检查stateOwner的当前状态是否具有“输入”映射。 If it has such a map, it will check if the input has a new State to change to, and invoke the onChangeState method. 如果它有这样的映射,它将检查输入是否有要更改的新State,并调用onChangeState方法。
I will suggest you have a default implementation of the StateChangeHandler, and of course sub classes that will define the state change behavior more explicitly. 我建议你有一个StateChangeHandler的默认实现,当然还有子类,它们将更明确地定义状态更改行为。

As I previously mentioned, all this can be loaded from an XML configuration, and using reflection you can instantitate StateChangeHandler objects based on their name (as mentioned at the XML) and that will be held in the repository. 正如我之前提到的,所有这些都可以从XML配置加载,并且使用反射,您可以根据其名称(如XML中所述)立即动态StateChangeHandler对象,并将保存在存储库中。


Efficiency and good performance rely and obtained using the following points: 使用以下几点依靠并获得效率和良好性能:
a. 一种。 The repository itself is stateless - no internal references of StateOwner should be kept. 存储库本身是无状态的 - 不应保留StateOwner的内部引用。
b. You load the XML once , when the system starts, after that you should work with in memory data structure. 在系统启动时加载XML一次,之后您应该在内存数据结构中使用。
c. C。 You will provide specific StateChangeHandler implementation only when needed, the default implementation should do basicaly nothing. 您将仅在需要时提供特定的StateChangeHandler实现,默认实现应该基本没有。
d. d。 No need to instantiate new objects of Handlers (as they should be stateless) 无需实例化处理程序的新对象(因为它们应该是无状态的)

This proposal isn't universal, it isn't UML compliant but for simple thing, it's a simple mean . 这个提议不是通用的,它不符合UML,简单来说,它是一个简单的意思

import java.util.HashMap;
import java.util.Map;

class Mobile1
{
   enum State {
      FIRST, SECOND, THIRD
   }

   enum Event {
      FIRST, SECOND, THIRD
   }

   public Mobile1() {       // initialization may be done by loading a file
      Map< Event, State > tr;
      tr = new HashMap<>();
      tr.put( Event.FIRST, State.SECOND );
      _fsm.put( State.FIRST, tr );
      tr = new HashMap<>();
      tr.put( Event.SECOND, State.THIRD );
      _fsm.put( State.SECOND, tr );
      tr = new HashMap<>();
      tr.put( Event.THIRD, State.FIRST );
      _fsm.put( State.THIRD, tr );
   }

   public void activity() {        // May be a long process, generating events,
      System.err.println( _state );// to opposite to "action()" see below
   }

   public void handleEvent( Event event ) {
      Map< Event, State > trs = _fsm.get( _state );
      if( trs != null ) {
         State futur = trs.get( event );
         if( futur != null ) {
            _state = futur;
           // here we may call "action()" a small piece of code executed
           // once per transition
         }
      }
   }

   private final Map<
      State, Map<
         Event, State >> _fsm   = new HashMap<>();
   private /* */ State   _state = State.FIRST;
}

public class FSM_Test {
   public static void main( String[] args ) {
      Mobile1 m1 = new Mobile1();
      m1.activity();
      m1.handleEvent( Mobile1.Event.FIRST );
      m1.activity();
      m1.handleEvent( Mobile1.Event.SECOND );
      m1.activity();
      m1.handleEvent( Mobile1.Event.FIRST );   // Event not handled
      m1.activity();
      m1.handleEvent( Mobile1.Event.THIRD );
      m1.activity();
   }
}

output: 输出:

FIRST
SECOND
THIRD
THIRD
FIRST

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM