我有一个围绕 HashMap (ConcurrentMap> slicesMap)组织的线程(实现可运行的类,称为AnalyzeTree)。该类遍历大型文本文件中的数据(此处称为树),并将地理坐标从其中解析到 HashMap。这个想法是一次处理一棵树,并根据键(只是代表时间的 Double 值)添加或增长值。
代码的相关部分如下所示:
// grow map entry if key exists
if (slicesMap.containsKey(sliceTime)) {
double[] imputedLocation = imputeValue(
location, parentLocation, sliceHeight,
nodeHeight, parentHeight, rate,
useTrueNoise, currentTreeNormalization,
precisionArray);
slicesMap.get(sliceTime).add(new Coordinates(imputedLocation[1], imputedLocation[0], 0.0));
// start new entry if no such key in the map
} else {
List<Coordinates> coords = new ArrayList<Coordinates>();
double[] imputedLocation = imputeValue(
location, parentLocation, sliceHeight,
nodeHeight, parentHeight, rate,
useTrueNoise, currentTreeNormalization,
precisionArray);
coords.add(new Coordinates(imputedLocation[1], imputedLocation[0], 0.0));
slicesMap.putIfAbsent(sliceTime, coords);
// slicesMap.put(sliceTime, coords);
}// END: key check
该类的调用方式如下(执行器为 ExecutorService executor = Executors.newFixedThreadPool(NTHREDS) ):
mrsd = new SpreadDate(mrsdString);
int readTrees = 1;
while (treesImporter.hasTree()) {
currentTree = (RootedTree) treesImporter.importNextTree();
executor.submit(new AnalyzeTree(currentTree,
precisionString, coordinatesName, rateString,
numberOfIntervals, treeRootHeight, timescaler,
mrsd, slicesMap, useTrueNoise));
// new AnalyzeTree(currentTree, precisionString,
// coordinatesName, rateString, numberOfIntervals,
// treeRootHeight, timescaler, mrsd, slicesMap,
// useTrueNoise).run();
readTrees++;
}// END: while has trees
现在这在并行执行时遇到了麻烦(按顺序运行的注释部分很好),我认为它可能会抛出 ConcurrentModificationException,但显然问题出在 mrsd (SpreadDate 对象的实例,它只是一个类)日期相关的计算)。
SpreadDate 类如下所示:
public class SpreadDate {
private Calendar cal;
private SimpleDateFormat formatter;
private Date stringdate;
public SpreadDate(String date) throws ParseException {
// if no era specified assume current era
String line[] = date.split(" ");
if (line.length == 1) {
StringBuilder properDateStringBuilder = new StringBuilder();
date = properDateStringBuilder.append(date).append(" AD").toString();
}
formatter = new SimpleDateFormat("yyyy-MM-dd G", Locale.US);
stringdate = formatter.parse(date);
cal = Calendar.getInstance();
}
public long plus(int days) {
cal.setTime(stringdate);
cal.add(Calendar.DATE, days);
return cal.getTimeInMillis();
}// END: plus
public long minus(int days) {
cal.setTime(stringdate);
cal.add(Calendar.DATE, -days); //line 39
return cal.getTimeInMillis();
}// END: minus
public long getTime() {
cal.setTime(stringdate);
return cal.getTimeInMillis();
}// END: getDate
}
以及抛出异常时的堆栈跟踪:
java.lang.ArrayIndexOutOfBoundsException: 58
at sun.util.calendar.BaseCalendar.getCalendarDateFromFixedDate(BaseCalendar.java:454)
at java.util.GregorianCalendar.computeFields(GregorianCalendar.java:2098)
at java.util.GregorianCalendar.computeFields(GregorianCalendar.java:2013)
at java.util.Calendar.setTimeInMillis(Calendar.java:1126)
at java.util.GregorianCalendar.add(GregorianCalendar.java:1020)
at utils.SpreadDate.minus(SpreadDate.java:39)
at templates.AnalyzeTree.run(AnalyzeTree.java:88)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)
at java.util.concurrent.FutureTask.run(FutureTask.java:166)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
at java.lang.Thread.run(Thread.java:636)
如果将初始化 mrsd 的部分移至AnalyzeTree 类,它运行时不会出现任何问题 - 但是每次该线程运行时初始化类的内存效率不是很高,因此我担心。该如何补救?
最佳答案
日历和日期是可变类的两个示例。您似乎在 ExecutorService 之间共享它们,并且您看到的结果是意外的。我建议为每个线程创建一个 SpreadDate 对象的新实例。
关于java - 并发和日历类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6455027/