我在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());
}
}
答案 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);
}
并处理它,以便主线程稍后尝试获取值。 (填充数组时)