在对象上同步似乎没有同步

时间:2015-10-29 12:20:06

标签: java multithreading synchronization synchronized java-threads

我运行的程序包含以下类(不仅如此,但这些是相关的问题)

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线程安全。

为什么输出不如我预期的那样?哪里有安全问题?

1 个答案:

答案 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的实例)。