简体   繁体   English

计时器任务无法启动

[英]Timer task does not start

I was writing code in the constructor of Class A to create an object of MetricData and start the timer in it. 我在Class A的构造函数中编写代码以创建MetricData对象并在其中启动计时器。 The timer will call method to fill the property array with random numbers every 2 seconds, and the data filling starts right on when start the timer. 计时器将每隔2秒调用一次方法以用随机数填充属性数组,并且在启动计时器时立即开始数据填充。 I got a NPE on propertyValue = propertyMetricData.getFirstDataPoint(); 我在propertyValue = propertyMetricData.getFirstDataPoint();上获得了NPE propertyValue = propertyMetricData.getFirstDataPoint(); which I assume there is no data in the array when calling that method, but when I'm in debug mode executing command by command, it sometimes works(around 4/10 times works). 我假设在调用该方法时数组中没有数据,但是当我在调试模式下按命令执行命令时,它有时会起作用(大约是4/10倍)。 So I guess maybe the problem has something to do with timing? 所以我想也许问题与时间有关吗? Like when calling getFirstDataPoint() the fillData() in the timer hasn't been called yet? 就像调用getFirstDataPoint()时一样,尚未调用计时器中的fillData()吗? I could not find where the problem is, so please help me, . 我找不到问题所在,所以请帮助我。

================================ Constructor of class A ================================ ============================== A类的构造函数=============== ==================

public A(){

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

================================ Class MetricData =================================== ===============================类MetricData ================ ===================

public class MetricData {

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

public MetricData(){

    startTimer();   
}

public ArrayList<Double> 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());
}
}

First, for safety reasons, synchronize all access to dataPoints , since you'll be accessing it from different threads. 首先,出于安全原因,请同步对dataPoints所有访问,因为您将从不同的线程访问它。 Some weird synchronization issues could be solved by that. 可以解决一些奇怪的同步问题。

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));
        }
    }
}
}

Secondly, you are executing your Timer asynchronously, so the second line on your A() constructor will not wait anything to run, so it'll most probably get nothing there. 其次,您正在异步执行Timer,因此A()构造函数的第二行将不等待任何内容运行,因此很可能在该处什么也没有。

It's a logic issue. 这是一个逻辑问题。 I don't know what you meant by this code, but maybe if you move the propertyValue = propertyMetricData.getFirstDataPoint(); 我不知道这段代码的含义,但是也许如果您移动propertyValue = propertyMetricData.getFirstDataPoint(); line from the constructor to a getter method would give you better results, since it'll be called only when needed, not on the object creation (and will give some time for the Timer to work): 从构造函数到getter方法的一行会给您带来更好的结果,因为仅在需要时才调用它,而不是在对象创建时调用(并且会为Timer工作提供一些时间):

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

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

But my best advice is to take a deep breath and review your logic. 但是,我最好的建议是深呼吸并检查您的逻辑。

The quick do it dirty solution would be to call Thread.Sleep : 快速做到这一点肮脏的解决办法是调用Thread.Sleep

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

This will let the time to your timer which runs in its own Thread to populate the ArrayList of points. 这将使时间流向您的计时器,该计时器在其自己的 Thread 运行以填充点的ArrayList

However, I agree with all that has been said in the answer of Everton. 但是,我同意埃弗顿回答中所说的一切。 You should probably review the logic so that it looks more elegant. 您可能应该检查一下逻辑,以使其看起来更优雅。 You could probably lock the Main Thread waiting for the ArrayList to be populated by values. 您可能会锁定主线程,以等待ArrayList被值填充。 The Producer Thread (in this case the timer) would then notify the Consumer Thread so that it could call the getFirstDataPoint safely. 然后,生产者线程(在这种情况下为计时器)将notify消费者线程,以便它可以安全地调用getFirstDataPoint

Or you could handle this with an Exception (I deliberately omit the synchronized) : 或者,您可以使用Exception处理这个问题(我故意省略了同步的):

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);
}

And handle it so that the Main Thread tries to get the value later. 并对其进行处理,以便主线程稍后尝试获取该值。 (when the Array is populated) (当数组被填充时)

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

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