这段代码是线程安全的吗?

我想处理客户端请求流。 每个请求都有其特殊类型。 首先,我需要初始化该类型的一些数据,然后我可以开始处理请求。 当客户端类型第一次出现时,我只是初始化相应的数据。 在此之后,使用该数据处理该类型的所有以下请求。

我需要以线程安全的方式执行此操作。

这是我写的代码。 它是线程安全的吗?

public class Test { private static Map clientTypesInitiated = new ConcurrentHashMap(); /* to process client request we need to create corresponding client type data. on the first signal we create that data, on the second - we process the request*/ void onClientRequestReceived(int clientTypeIndex) { if (clientTypesInitiated.put(clientTypeIndex, "") == null) { //new client type index arrived, this type was never processed //process data for that client type and put it into the map of types Object clientTypeData = createClientTypeData(clientTypeIndex); clientTypesInitiated.put(clientTypeIndex, clientTypeData); } else { //already existing index - we already have results and we can use them processClientUsingClientTypeData(clientTypesInitiated.get(clientTypeIndex)); } } Object createClientTypeData(int clientIndex) {return new Object();} void processClientUsingClientTypeData(Object clientTypeData) {} } 

一方面,ConcurrentHashMap不能为同一个A生成map.put(A,B)== null两次。另一方面,赋值和比较运算不是线程安全的。

那么这段代码还可以吗? 如果没有,我该如何解决?

更新:我接受了Martin Serrano的答案,因为他的代码是线程安全的,并且不容易出现双重初始化问题。 但我想指出,我没有找到任何与我的版本有关的问题,作为下面的答案发布,我的版本不需要同步。

此代码不是线程安全的,因为

 //already existing index - we already have results and we can use them processClientUsingClientTypeData(clientTypesInitiated.get(clientTypeIndex)); 

有可能获得您在看跌期权中暂时插入的“”值。

因此,这段代码可以成为线程安全的:

 public class Test { private static Map clientTypesInitiated = new ConcurrentHashMap(); /* to process client request we need to create corresponding client type data. on the first signal we create that data, on the second - we process the request*/ void onClientRequestReceived(int clientTypeIndex) { Object clientTypeData = clientTypesInitiated.get(clientTypeIndex); if (clientTypeData == null) { synchronized (clientTypesInitiated) { clientTypeData = clientTypesInitiated.get(clientTypeIndex); if (clientTypeData == null) { //new client type index arrived, this type was never processed //process data for that client type and put it into the map of types clientTypeData = createClientTypeData(clientTypeIndex); clientTypesInitiated.put(clientTypeIndex, clientTypeData); } } } processClientUsingClientTypeData(clientTypeData); } Object createClientTypeData(int clientIndex) {return new Object();} void processClientUsingClientTypeData(Object clientTypeData) {} 

}

不,我不认为它仍然是线程安全的。

您需要将put操作包装在synchronized块中。

根据ConcurrentHashMap的javadoc

检索操作(包括get)通常不会阻塞,因此可能与更新操作(包括put和remove)重叠。

你应该在这里使用putIfAbsent,这个操作的语义类似于CAS,它肯定是primefaces的。 既然它是primefaces的 – 那么你就没有内部任务和比较的问题。

当前代码不是线程安全的。

创建ConcurrentHashMap不使用synchronized块,尽管将它用作同步块的锁是可以的,但效率不高。 如果必须使用ConcurrentHashMap ,正确用法如下;

 private static Map clientTypesInitiated = new ConcurrentHashMap(); void onClientRequestReceived(int clientTypeIndex) { Object clientTypeData = clientTypesInitiated.get(clientTypeIndex); if (clientTypeData == null) { Object newClientTypeData = createClientTypeData(clientTypeIndex); clientTypeData = clientTypesInitiated.putIfAbsent(clientTypeIndex, newClientTypeData); // if clientTypeData is null, then put successful, otherwise, use the old one if (clientTypeData == null) { clientTypeData = newClientTypeData; } } processClientUsingClientTypeData(clientTypeData); } 

说实话,上面的代码可能会多次创建clientTypeData (因为竞争条件),尽管它不使用synchronized块。 因此,您应该测量创建clientTypeData成本是clientTypeData ,如果它过于昂贵以至于不使用synchronized块,那么您甚至不需要使用ConcurrentHashMap 。 使用普通的HashMapsynchronized块。

不,它不是,如果您需要整个方法是线程安全的,请将其声明为同步,或使用synchronized块来放置您的关键代码。

当前代码不是线程安全的,因为在if和下一个put之间可能发生上下文切换。 您需要将clientTypesInitiated为ConcurrentHashMap类型而不是接口Map。 然后使用putIfAbsent方法

我认为代码根本无法正常工作。 看:

 void onClientRequestReceived(int clientTypeIndex) { // the line below ensures the existing data is lost even if was not null if (clientTypesInitiated.put(clientTypeIndex, "") == null) { Object clientTypeData = createClientTypeData(clientTypeIndex); clientTypesInitiated.put(clientTypeIndex, clientTypeData); } else { processClientUsingClientTypeData(clientTypesInitiated.get(clientTypeIndex)); } } 

我想你更喜欢那里的get方法调用。

我建议这样的方法:

 void onClientRequestReceived(int clientTypeIndex) { Object clientTypeData; synchronized (clientTypesInitiated) { if ((clientTypeData = clientTypesInitiated.get(clientTypeIndex)) == null) { clientTypeData = createClientTypeData(clientTypeIndex); clientTypesInitiated.put(clientTypeIndex, clientTypeData); } } processClientUsingClientTypeData(clientTypeData); } 

putIfAbsent效果很好,但有时需要多次重复初始化。 具有双重检查锁定的同步块可能可以解决此问题。

但是这里有另一个基于AtomicIntegerArrays的选项:

 public class Test { private static Map clientTypesInitiated = new ConcurrentHashMap(); private static final int CLIENT_TYPES_NUMBER = 10; private static AtomicIntegerArray clientTypesInitStarted = new AtomicIntegerArray(CLIENT_TYPES_NUMBER); private static AtomicIntegerArray clientTypesInitFinished = new AtomicIntegerArray(CLIENT_TYPES_NUMBER); void onClientRequestReceived(int clientTypeIndex) { int isInitStarted = clientTypesInitStarted.getAndIncrement(clientTypeIndex); int isInitFinished = clientTypesInitFinished.get(clientTypeIndex); if (isInitStarted == 0) { //new client type index arrived, this type was never processed //process data for that client type and put it into the map of types Object clientTypeData = createClientTypeData(clientTypeIndex); clientTypesInitiated.put(clientTypeIndex, clientTypeData); clientTypesInitFinished.getAndIncrement(clientTypeIndex); } else if (isInitFinished > 0) { //already existing index - we already have results and we can use them processClientUsingClientTypeData(clientTypesInitiated.get(clientTypeIndex)); } } Object createClientTypeData(int clientIndex) { return new Object(); } void processClientUsingClientTypeData(Object clientTypeData) { } } 

任何意见?