2

所以我创建了一个可以包含在我的程序类中的类,并从一个Thread基本上会在某些变量发生变化时提醒我的类开始。我想知道是否有人有更好的想法来快速有效地获取此类信息,如果没有,他们是否对这门课有任何建议。仅供参考,该alert()方法基本上是一个简单的 System.out.println()。

public class OutputState extends Thread{

  String   className = this.getClass().toString().replace("class ","").replace("$OutputState",""); 
  String[] desc = {"canvasWidth","canvasHeight","relativeCenterX","relativeCenterY","zoom","hAdjust","vAdjust"};
  int[]    delay = {0,0,99,99,0,0,0,};
  Object[] var;
  int maxDescLength = 0;

  public void init(){
     Object[] v = {canvasWidth,canvasHeight,relativeCenterX,relativeCenterY,zoom,hAdjust,vAdjust};
     var = v;
  }

  public void run(){
     init();
     while(true){
        boolean newLine = false;
        Object[] v = {canvasWidth,canvasHeight,relativeCenterX,relativeCenterY,zoom,hAdjust,vAdjust};
        for(int i = 0; i < var.length; i++){
           if(maxDescLength < desc[i].length()){
              maxDescLength = desc[i].length();
           }
           if(!var[i].equals(v[i])){
              var[i]=v[i];
              String spaces = " ";
              int count = desc[i].length()+1;
              while(count <= maxDescLength){
                 spaces += " ";
                 count++;
              }
              alert(className + "." + desc[i] + spaces + "= " + var[i]);
              newLine = true;
              if(delay[i] > 0){try{Thread.sleep(delay[i]);}catch(InterruptedException e){}}
           }
        }
        if(newLine){
           alert("------------------------------------");
        }
        try{Thread.sleep(1);}catch(InterruptedException e){}
     }   
  }
} 

基本概念是v设置为您要跟踪的变量,并将继续使用程序中不断变化的变量进行自我更新,var并将包含OutputState最后知道的变量。每当v更改中的任何内容var都会捕获它并输出自定义消息。我知道很多人会发现类似的东西很有用,这让我相信可能有更好的方法我还没有找到。我将不胜感激任何意见!

4

3 回答 3

1

对于每个包含您想要跟踪的属性的 bean,您可以添加 PropertyChangeSupport 和一个向其添加 PropertyChangeListener 的方法。然后在设置器中调用 PropertyChangeSupport 上的 firePropertyChange。使用此配置,您甚至可以使用配置来决定要侦听哪些对象,并根据需要动态添加和删除侦听器。您不需要单独的线程,也不需要循环和睡眠。这是一个演示的小样本。

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;

public class ChangeTest {

  private String val1;
  private String val2;
  private PropertyChangeSupport pcs = new PropertyChangeSupport(this);

  public String getVal1() {
    return val1;
  }

  public void setVal1(String val1) {
    pcs.firePropertyChange("val1", this.val1, val1);
    this.val1 = val1;
  }

  public String getVal2() {
    return val2;
  }

  public void setVal2(String val2) {
    pcs.firePropertyChange("val2", this.val2, val2);
    this.val2 = val2;
  }

  public void addPropertyChangeListener(PropertyChangeListener listener ) {
    pcs.addPropertyChangeListener(listener);
  }

  public static void main( String[] args ) {
    ChangeTest test = new ChangeTest();
    test.addPropertyChangeListener(new Listener());
    for ( int i = 0; i < 5; i++ ) {
      test.setVal1("value " + i );
      test.setVal2(test.getVal1() + test.getVal1());
    }
  }

  public static class Listener implements PropertyChangeListener {

    @Override
    public void propertyChange(PropertyChangeEvent evt) {
  System.out.println( evt.getPropertyName() + " changed from '" + evt.getOldValue() + "' to '" + evt.getNewValue() + "'" );
    }
  }
}

该程序的输出是:

val1 changed from 'null' to 'value 0'
val2 changed from 'null' to 'value 0value 0'
val1 changed from 'value 0' to 'value 1'
val2 changed from 'value 0value 0' to 'value 1value 1'
val1 changed from 'value 1' to 'value 2'
val2 changed from 'value 1value 1' to 'value 2value 2'
val1 changed from 'value 2' to 'value 3'
val2 changed from 'value 2value 2' to 'value 3value 3'
val1 changed from 'value 3' to 'value 4'
val2 changed from 'value 3value 3' to 'value 4value 4'
于 2013-09-27T13:35:39.033 回答
1

可靠地判断变量何时更改的唯一方法是将其声明为private并且仅通过 setter 方法对其进行更改。然后你编写setter它来测试每个调用是否会改变变量的状态。

例如:

private int summit;

public void setSummit(int value) {
    if (value != summit) {
        System.err.println("Ey up: summit's changed!");
    }
}

如果变量的类型是数组,则不可能可靠地判断数组元素何时发生变化……除非您隐藏数组以防止直接访问。


如果你愿意使用 AOP 或其他形式的代码注入,你也许可以在不修改源代码的情况下做这种事情。但是在运行时,您将有效地执行上述操作。

于 2013-09-27T13:27:16.900 回答
1

没有一种简单的方法可以完成您正在尝试做的事情。即使您循环(就像您目前正在做的那样),您不仅会减慢其余的计算速度(由于总线流量),而且还可能会错过一些更新(如果监视器线程的调度频率较低或比较繁忙)处理器而不是工作线程)。

就像评论中指出的那样,您最好使用一些面向方面的编程语言,或 Java 代理,或更简单的委托模式。无论哪种方式,您都可能需要修改现有代码。

interface IWorkState {
    //Declares the method of WorkState
}

class WorkState implements IWorkState {
    //Whatever java bean
}

class WorkTask implements Runnable {
    private final IWorkState state;

    public void addStateObserver(IWorkState initialState) {
         state = initialState
    }

    @Override
    public void run() {
        //Task instructions here
        //instructions should operate on the state variable
    }
}

class VarMonitor {
    public void update(Object changed) {
        //subject has changed, react to it
        eventQueue.submit(/* a Runnable that handles the state change,
                           with embedded information about the change */);

    }
}

class MainClass {
    public static void main(String[] args) {
        final VarMonitor mon = new VarMonitor();
        WorkTask t = new WorkTask(Proxy.newProxyInstance(getClass().getClassLoader(), new Class[] {IWorkState.class}, new InvocationHandler() {
            private final WorkState realWorkState = new WorkState();
            Object invoke(Object proxy, Method method, Object[] args) {
                Object res = method.invoke(realWorkState, args);
                //An annotation would work better
                if (method.getName().substring(0,3).equals("set")) {
                    mon.update(this);
                }
            }
        });
    }
}

第三种选择是使用子类java.util.Observable,当然还有观察者模式。就像是:

class WorkState extends java.util.Observable {
    //A thread-safe observable java bean
}

class WorkTask implements Runnable {
    private final WorkState state = new WorkState();

    public void addStateObserver(Observer ob) {
         state.addObserver(ob);
    }

    @Override
    public void run() {
        //Task instructions here
        //instructions should operate on the state variable
    }
}

class VarMonitor implements Observer {
    private final ExecutorService eventQueue
            = Executors.newSingleThreadExecutor();

    @Override
    public void update(Observable subject, Object info) {
        //subject has changed, react to it
        eventQueue.submit(/* a Runnable that handles the state change,
                           with embedded information about the change */);

    }
}

如果您将此模式与不可变对象一起使用,则可以轻松实现 ObservableReference 类,该类适用于您的所有对象。

PS:之前没提过,但是多线程访问共享变量一定要同步,或者变量要么是线程安全的要么是易失的。您不能盲目地轮询共享变量,否则您可能会得到一些错误的结果(例如看到部分更新的对象)。

于 2013-09-27T13:30:17.553 回答