Timer-Task startet nicht

Ich schrieb Code im Konstruktor von Class A , um ein Objekt von MetricData zu erstellen und den darin enthaltenen Timer zu starten. Der Timer ruft die Methode auf, um das Eigenschaftsfeld alle 2 Sekunden mit Zufallszahlen zu füllen, und die Datenbefüllung beginnt direkt beim Start des Timers. Ich habe eine NPE auf propertyValue = propertyMetricData.getFirstDataPoint (); was ich nehme an, dass keine Daten im Array beim Aufruf dieser Methode, aber wenn ich im Debug-Modus Ausführung von Befehl für Befehl manchmal funktioniert (ca. 4/10 mal funktioniert). Ich denke, vielleicht hat das Problem etwas mit Timing zu tun? Wie beim Aufruf von getFirstDataPoint() wurde der fillData() im Timer noch nicht aufgerufen? Ich konnte nicht finden, wo das Problem ist, also bitte hilf mir,.

============================= Konstruktor der Klasse A ============== ====================

public A(){

    propertyMetricData = new MetricData();
    propertyValue = propertyMetricData.getFirstDataPoint();
{

============================== Klasse MetricData ================ ======================

public class MetricData {

private Timer timer = new Timer();;
private DecimalFormat df = new DecimalFormat("###.##");
private Random rand = new Random();
private ArrayList dataPoints = new ArrayList();

public MetricData(){

    startTimer();   
}

public ArrayList getDataPoints(){
    return dataPoints;
}

public Double getFirstDataPoint(){
    return dataPoints.get(0);
}

private void startTimer(){

    timer.scheduleAtFixedRate(new TimerTask(){
        @Override
        public void run() {
            fillData();
        }
    }, 0, 2*1000);  
}

private void fillData(){

    dataPoints.clear();

    for(int i=0; i<100; i++){
        dataPoints.add(genRanNum(1,10));
    }
}

private Double genRanNum(int min, int max){

    double number = min + (rand.nextDouble()*(max - min));

    return Double.valueOf(df.format(number));
}

public void testTimer(){
    System.out.println(getFirstDataPoint());
}
}
0

2 Antworten

Synchronisieren Sie zunächst aus Sicherheitsgründen alle Zugriffe auf dataPoints , da Sie von verschiedenen Threads darauf zugreifen. Einige seltsame Synchronisationsprobleme könnten dadurch gelöst werden.

public class MetricData {
...

public Double getFirstDataPoint(){
    synchronized(dataPoints) {
        return dataPoints.get(0);
    }
}

private void fillData(){
    synchronized (dataPoints) {    
        dataPoints.clear();

        for(int i=0; i<100; i++){
            dataPoints.add(genRanNum(1,10));
        }
    }
}
}

Zweitens, Sie führen Ihren Timer asynchron aus, so dass die zweite Zeile Ihres A() -Konstruktors nichts zum Ausführen erwartet, so dass er dort höchstwahrscheinlich nichts erhalten wird.

Es ist ein logisches Problem. Ich weiß nicht, was Sie mit diesem Code meinen, aber vielleicht, wenn Sie die Eigenschaft propertyValue = propertyMetricData.getFirstDataPoint (); vom Konstruktor in eine Getter-Methode verschieben, würden Sie bessere Ergebnisse erzielen, da sie ll kann nur bei Bedarf aufgerufen werden, nicht bei der Objekterstellung (und gibt dem Timer Zeit, zu arbeiten):

public A() {
    propertyMetricData = new MetricData();
{

public Double getPropertyValue() {
    return propertyMetricData.getFirstDataPoint();
}

Aber mein bester Rat ist, tief durchzuatmen und Ihre Logik zu überprüfen.

0
hinzugefügt
Danke Everton Agner. Kannst du mehr über den Thread erklären? Ich habe keinen separaten Thread erstellt, also Java nur für verschiedene Objekte standardmäßig erstellen?
hinzugefügt der Autor YueQi Li, Quelle
Nein, Timer hat einen Thread, der die geplanten TimerTasks ausführt. Ich habe ein bisschen mehr meinen zweiten Rat zur Antwort ausgearbeitet, seht es euch an.
hinzugefügt der Autor everton, Quelle

Die schnelle do it dirty Lösung wäre, Thread.Sleep aufzurufen:

propertyMetricData = new MetricData();
Thread.sleep(2000);
propertyValue = propertyMetricData.getFirstDataPoint();

Dadurch wird die Zeit für Ihren Timer, der in einem eigenen -Thread ausgeführt wird, zum Auffüllen der ArrayList von Punkten verwendet.

Ich stimme jedoch allem zu, was in der Antwort von Everton gesagt wurde. Sie sollten die Logik wahrscheinlich überprüfen, damit sie eleganter aussieht. Sie könnten wahrscheinlich den Haupt-Thread sperren, der darauf wartet, dass die ArrayList von Werten aufgefüllt wird. Der Producer-Thread (in diesem Fall der Timer) würde dann den Consumer-Thread benachrichtigen , damit er den getFirstDataPoint sicher aufrufen kann.

Oder du könntest das mit einer Exception umgehen (ich verzichte bewusst auf das Synchronisierte):

public Double getFirstDataPoint() throws Exception {//Or custom exception
    if(dataPoints.get(0).equals(null))
         throw new Exception("Array non populated yet!");
    return dataPoints.get(0);
}

Und behandeln Sie es so, dass der Haupt-Thread versucht, den Wert später zu erhalten. (wenn das Array bestückt ist)

0
hinzugefügt