Java 在一个对象上同步似乎是';它没有同步

Java 在一个对象上同步似乎是';它没有同步,java,multithreading,synchronization,synchronized,java-threads,Java,Multithreading,Synchronization,Synchronized,Java Threads,我运行了一个包含以下类的程序(不仅如此,这些都是与问题相关的类) 在Results类下,我有一个同步的LinkedHashMap,例如: private static Map<Integer,Result> resultsHashMap=Collections.synchronizedMap(new LinkedHashMap<Integer, Result>()); 以及同步getter方法,如下所示: public static int getUniqueId

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

在Results类下,我有一个同步的LinkedHashMap,例如:

private static Map<Integer,Result>    resultsHashMap=Collections.synchronizedMap(new LinkedHashMap<Integer, Result>());
以及同步getter方法,如下所示:

public static int getUniqueIdResult() {
    synchronized (Lock.lock) {
        return uniqueIdResult;
    }

}
UniquedResult的定义如下:

private static int uniqueIdResult=0;
我还有一个锁类,由以下对象组成:

public static final Lock lock=new Lock();
现在,这是我关注的重要问题。在我的程序中,我有下面两行代码,它们创建一个结果并将其放入HashMap

Result result = new Result();
Results.getResultsHashMap().put(Result.getUniqueIdResult(), result);
我尝试用不同的线程数运行我的程序。当它使用1个线程运行时,输出与我期望的一样(具体来说,但不一定重要,Results.resultsHashMap包含433个键,这是应该的,这些键从1开始)

但当我用不同数量的线程运行它时,它会给出不同的输出。例如,使用6个线程运行时,每次提供的键数不同,有时为430,有时为428,有时为427,等等。。并且开始键并不总是与键的总数相关(例如,总键数-开始键数+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方法时,如果没有同步结果创建和对hashMap的插入,则多线程的输出会给出错误的输出。
此外,当只同步一行(创建结果并放入hashMap)时,在多个线程下输出不一致

但是,当我同步这两行时(创建结果并放入地图),如下所示:

无论我使用多少线程,输出都是完美的

另外,我将注意到,只有在所有线程都完成之后,才通过对所有创建的线程使用join方法打印输出

所以我的问题是:
据我所知,在同步这两行之前(创建结果并放入hashMap),我所有的关键部分,例如,更改并获取UniquedResult,获取结果hashMap(正如我提到的,我也尝试过同步这个getter方法)都在同一个对象上同步,另外,在将hashMap与Collections.synchronizedMap放在一起时,我使用了一种更安全的方法,据我所知,这将使hashMap线程安全


那么,为什么输出不是我所期望的呢?哪里有安全问题?

这些线路周围没有排除:

Result result = new Result();
Results.getResultsHashMap().put(Result.getUniqueIdResult(), result);
如果有4个线程,它们可能都会执行第一行(将
uniqueIdResult
变量增加四倍),然后都会执行第二行(此时它们都会从
getUniqueIdResult()
中看到相同的返回值)。这就解释了当你有4个(或更多)线程时,你的密钥是如何从4开始的

因为有多个线程潜在地(并且不可预测地)存储到同一个键,所以在映射中也会有数量可变的条目

您可能应该从
结果
类构造函数中删除增量,而是在
getUniqueIdResult
方法中执行:

public static int getUniqueIdResult() {
    synchronized (Lock.lock) {
        return ++uniqueIdResult;
    }
}

(完成后,不再需要创建
Result
的实例)。

这些行没有排除:

Result result = new Result();
Results.getResultsHashMap().put(Result.getUniqueIdResult(), result);
如果有4个线程,它们可能都会执行第一行(将
uniqueIdResult
变量增加四倍),然后都会执行第二行(此时它们都会从
getUniqueIdResult()
中看到相同的返回值)。这就解释了当你有4个(或更多)线程时,你的密钥是如何从4开始的

因为有多个线程潜在地(并且不可预测地)存储到同一个键,所以在映射中也会有数量可变的条目

您可能应该从
结果
类构造函数中删除增量,而是在
getUniqueIdResult
方法中执行:

public static int getUniqueIdResult() {
    synchronized (Lock.lock) {
        return ++uniqueIdResult;
    }
}

(这样做之后,就不再需要创建
结果的实例了)。

“从4开始(至少在我选中时)”这是什么意思?听起来你不确定是从哪个号码开始的。请提供可复制的代码。我的意思是,在我在4个线程上运行程序的时候(大约10次),这就是输出。由于输出并不总是一致的(对于6个线程,我注意到在不同的执行中有不同的输出),所以我假设它也可能是不同的数字。无论如何,我从我的问题中删除了这一部分,因为它可能不清楚。“从4开始(至少在我检查时)”这是什么意思?听起来你不确定是从哪个号码开始的。请提供可复制的代码。我的意思是,在我在4个线程上运行程序的时候(大约10次),这就是输出。由于输出并不总是一致的(对于6个线程,我注意到在不同的执行中有不同的输出),所以我假设它也可能是不同的数字。无论如何,我从我的问题中删除了这一部分,因为它可能不清楚。
public static int getUniqueIdResult() {
    synchronized (Lock.lock) {
        return ++uniqueIdResult;
    }
}