计时器任务无法启动

时间:2013-12-16 17:03:54

标签: java timer constructor

我在Class A的构造函数中编写代码来创建MetricData的对象并在其中启动计时器。计时器将调用方法每2秒用随机数填充属性数组,并在启动计时器时立即开始数据填充。我在propertyValue = propertyMetricData.getFirstDataPoint();上获得了一个NPE,我假设在调用该方法时数组中没有数据,但是当我在调试模式下执行命令时,它有时会工作(大约4/10次工作)。所以我想这个问题可能与时间有关?就像在调用getFirstDataPoint()时,计时器中的fillData()尚未被调用?我找不到问题所在,所以请帮帮我。

================================ A类构造函数=========== =====================

public A(){

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

================================ Class 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());
}
}

2 个答案:

答案 0 :(得分:2)

首先,出于安全原因,请同步对dataPoints的所有访问,因为您将从不同的线程访问它。一些奇怪的同步问题可以解决。

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

其次,你是异步执行你的Timer,所以你A()构造函数的第二行不会等待任何东西运行,所以它很可能什么都没有。

这是一个逻辑问题。我不知道你的代码是什么意思,但是如果你将propertyValue = propertyMetricData.getFirstDataPoint();行从构造函数移动到getter方法会给你更好的结果,因为它只在需要时调用,而不是在对象创建(并为Timer提供一些时间):

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

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

但我最好的建议是深呼吸并检查你的逻辑。

答案 1 :(得分:1)

快速做脏话解决方案是致电Thread.Sleep

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

这会让你的计时器在自己的 Thread中运行,以填充ArrayList点。

然而,我同意埃弗顿答案中所说的一切。您应该检查逻辑,使其看起来更优雅。您可能会锁定主线程,等待ArrayList由值填充。然后,生产者线程(在这种情况下是计时器)将notify消费者线程,以便它可以安全地调用getFirstDataPoint

或者您可以使用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);
}

并处理它,以便主线程稍后尝试获取值。 (填充数组时)

相关问题