我有一个类,其中我有一个ConcurrentHashMap
,每30秒由一个线程更新,然后我通过调用ConcurrentHashMap
从同一个getNextSocket()
读取多个读者线程方法
下面是我的单例类,它在初始化时调用connectToSockets()
方法来填充我的ConcurrentHashMap
,然后启动一个后台线程,通过调用updateSockets()
方法每30秒更新一次相同的映射。
然后从多个线程我调用getNextSocket()
方法获取下一个可用的实时套接字,该套接字使用相同的映射来获取信息。我还有SocketInfo
类,它是不可变的,包含所有套接字的状态,无论它们是否存在。
public class SocketHolder {
private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
private final Map<DatacenterEnum, List<SocketInfo>> liveSocketsByDc = new ConcurrentHashMap<>();
// Lazy Loaded Singleton Pattern
private static class Holder {
private static final SocketHolder INSTANCE = new SocketHolder();
}
public static SocketHolder getInstance() {
return Holder.INSTANCE;
}
private SocketHolder() {
connectToSockets();
scheduler.scheduleAtFixedRate(new Runnable() {
public void run() {
updateSockets();
}
}, 30, 30, TimeUnit.SECONDS);
}
private void connectToSockets() {
Map<DatacenterEnum, ImmutableList<String>> socketsByDc = TestUtils.SERVERS;
for (Map.Entry<DatacenterEnum, ImmutableList<String>> entry : socketsByDc.entrySet()) {
List<SocketInfo> addedColoSockets = connect(entry.getKey(), entry.getValue(), ZMQ.PUSH);
liveSocketsByDc.put(entry.getKey(), addedColoSockets);
}
}
private List<SocketInfo> connect(DatacenterEnum dc, List<String> addresses, int socketType) {
List<SocketInfo> socketList = new ArrayList<>();
// ... some code here
return socketList;
}
// called from multiple reader threads to get next live available socket
public Optional<SocketInfo> getNextSocket() {
Optional<SocketInfo> liveSocket = getLiveSocket(liveSocketsByDc.get(DatacenterEnum.CORP));
return liveSocket;
}
private Optional<SocketInfo> getLiveSocket(final List<SocketInfo> listOfEndPoints) {
if (!CollectionUtils.isEmpty(listOfEndPoints)) {
Collections.shuffle(listOfEndPoints);
for (SocketInfo obj : listOfEndPoints) {
if (obj.isLive()) {
return Optional.of(obj);
}
}
}
return Optional.absent();
}
// update CHM map every 30 seconds
private void updateSockets() {
Map<DatacenterEnum, ImmutableList<String>> socketsByDc = TestUtils.SERVERS;
for (Entry<DatacenterEnum, ImmutableList<String>> entry : socketsByDc.entrySet()) {
List<SocketInfo> liveSockets = liveSocketsByDc.get(entry.getKey());
List<SocketInfo> liveUpdatedSockets = new ArrayList<>();
for (SocketInfo liveSocket : liveSockets) {
Socket socket = liveSocket.getSocket();
String endpoint = liveSocket.getEndpoint();
boolean sent = ....;
boolean isLive = sent ? true : false;
// is this right here? or will it cause any race condition?
SocketInfo state = new SocketInfo(socket, liveSocket.getContext(), endpoint, isLive);
liveUpdatedSockets.add(state);
}
// update map with new liveUpdatedSockets
liveSocketsByDc.put(entry.getKey(), liveUpdatedSockets);
}
}
}
问题:
我的上述代码线程是否安全且updateSockets()
和getNextSocket()
方法中没有竞争条件?
在我的updateSockets()
方法中,我从List<SocketInfo>
ConcurrentHashMap中提取liveSocketsByDc
,它已在connectToSockets()
方法初始化之前填充,或者在{{1然后我迭代相同的列表updateSockets()
并创建一个新的liveSockets
对象,具体取决于SocketInfo
是真还是假。然后我用这个新的isLive
对象更新liveSocketsByDc
ConcurrentHashMap。这看起来不错吗?因为来自多个读者线程,我将调用SocketInfo
方法来调用getNextSocket()
方法,该方法使用相同的映射来获取下一个可用的实时套接字。
我正在迭代getLiveSocket
列表,然后通过更改liveSockets
字段创建新的SocketInfo
对象,其他内容将保持不变。这是对的吗?
如果存在线程安全问题,解决此问题的最佳方法是什么?
答案 0 :(得分:1)
下面:
List<SocketInfo> liveSockets = liveSocketsByDc.get(entry.getKey());
您的不同主题可能并行写入/读取相同列表对象。
所以:不是线程安全的。拥有“外部”线程安全数据结构没有帮助;当该线程安全的东西包含数据时......这不是线程安全的;但随后“继续”工作了!