我运行的程序包含以下类(不仅如此,但这些是相关的问题)
在 Results 类下,我有一个同步的LinkedHashMap,例如:
private static Map<Integer,Result> resultsHashMap=Collections.synchronizedMap(new LinkedHashMap<Integer, Result>());
和一个getter方法:
public static Map<Integer,Result> getResultsHashMap() {
return resultsHashMap;
}
同样,我在 Result 类中有一个带有此同步代码的构造函数:
public Result(){
synchronized (Lock.lock) {
uniqueIdResult++;
}
}
和同步的getter方法:
public static int getUniqueIdResult() {
synchronized (Lock.lock) {
return uniqueIdResult;
}
}
uniqueIdResult定义如下:
private static int uniqueIdResult=0;
另外我有一个Lock类包含这个Object:
public static final Lock lock=new Lock();
现在,这是我追求的重要问题。在我的程序中,我有接下来的两行,它们正在创建一个Result并将其放入HashMap
Result result = new Result();
Results.getResultsHashMap().put(Result.getUniqueIdResult(), result);
我尝试使用不同数量的线程运行我的程序。当它以1个线程运行时,输出正如我所期望的那样(具体而言,但不一定重要,Results.resultsHashMap包含433个键,这应该是,键是从1开始)。
但是当我使用不同数量的线程运行它时,它会提供不同的输出。例如,使用6个线程运行每次给出不同数量的键,有时是430,有时是428,有时是427等。并且起始键并不总是与键的总数相关(例如,total_number_of_keys-starting_key_number + 1,其中在我看来,在我看来是一些模式,但意识到它不是)
迭代是这样的:
int counterOfResults=0;
for (Integer key : Results.getResultsHashMap().keySet()) {
System.out.println(key + " " + Results.getResultsHashMap().get(key));
counterOfResults++;
}
System.out.println(counterOfResults);
当同步获取hashMap 的getter方法时,如果没有Result创建和hashMap的同步,多输出的输出会产生错误的输出。
此外,仅同步其中一行(创建Result并放入hashMap)时,输出在多个线程下不一致。
然而,当我同步两个这些行(创建结果并放入地图)时,如下所示:
Result result;
synchronized (Lock.lock) {
result = new Result(currentLineTimeNationalityNameYearofbirth.getName(),currentLineTimeNationalityNameYearofbirth.getTime(),citycompetionwas,date,distance,stroke,gender,kindofpool);
Results.getResultsHashMap().put(Result.getUniqueIdResult(), result);
}
无论我使用多少线程,输出都是完美的。
另外,我会注意到,只有在所有线程完成后才会打印输出,对所有创建的线程使用 join 方法。
所以我的问题是:
据我所知,在同步2行(创建结果并放入hashMap)所有关键部分之前,例如,更改并获取 uniqueIdResult ,获取< em> resultsHashMap (正如我所提到的,我也试过同步这个getter方法)正在同一个对象上进行同步,另外我在使用Collections.synchronizedMap放置hashMap时还提出了一个更安全的方法,就我而言知道,应该使hashMap线程安全。
为什么输出不如我预期的那样?哪里有安全问题?
答案 0 :(得分:4)
围绕这些方面没有排除:
Result result = new Result();
Results.getResultsHashMap().put(Result.getUniqueIdResult(), result);
如果你有4个线程,它们可能都会执行第一行(这将使uniqueIdResult
变量增加四次),然后所有执行第二行(此时它们都会看到相同的返回值)来自getUniqueIdResult()
)。这解释了当你有4个(或更多)线程时,你的密钥如何从4开始。
由于您有多个线程可能(并且不可预测)存储到同一个键,因此您的地图中也会有可变数量的条目。
您应该从Result
类构造函数中删除增量,而是使用getUniqueIdResult
方法执行此操作:
public static int getUniqueIdResult() {
synchronized (Lock.lock) {
return ++uniqueIdResult;
}
}
(完成后,根本不再需要创建Result
的实例)。