나는 맵을 채우는 클래스를 가지고있다. 하나의 백그라운드 스레드에서 매 30 초마다 하나의 백그라운드 스레드로부터 getNextSocket
을 얻는다.이 맵핑을 얻기 위해 여러 개의 리더 스레드가 호출 할 것이다. 이 정보.단일 스레드에서 해시 맵을 수정하고 여러 스레드에서 읽기?
public class SocketManager {
private static final Random random = new Random();
private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
private final Map<Datacenters, List<SocketHolder>> liveSocketsByDatacenter = new HashMap<>();
private final ZContext ctx = new ZContext();
// Lazy Loaded Singleton Pattern
private static class Holder {
private static final SocketManager instance = new SocketManager();
}
public static SocketManager getInstance() {
return Holder.instance;
}
private SocketManager() {
connectToZMQSockets();
scheduler.scheduleAtFixedRate(new Runnable() {
public void run() {
updateLiveSockets();
}
}, 30, 30, TimeUnit.SECONDS);
}
private void connectToZMQSockets() {
Map<Datacenters, ImmutableList<String>> socketsByDatacenter = Utils.SERVERS;
for (Map.Entry<Datacenters, ImmutableList<String>> entry : socketsByDatacenter.entrySet()) {
List<SocketHolder> addedColoSockets = connect(entry.getKey(), entry.getValue(), ZMQ.PUSH);
liveSocketsByDatacenter.put(entry.getKey(), addedColoSockets);
}
}
private List<SocketHolder> connect(Datacenters colo, List<String> addresses, int socketType) {
List<SocketHolder> socketList = new ArrayList<>();
for (String address : addresses) {
try {
Socket client = ctx.createSocket(socketType);
// Set random identity to make tracing easier
String identity = String.format("%04X-%04X", random.nextInt(), random.nextInt());
client.setIdentity(identity.getBytes(ZMQ.CHARSET));
client.setTCPKeepAlive(1);
client.setSendTimeOut(7);
client.setLinger(0);
client.connect(address);
SocketHolder zmq = new SocketHolder(client, ctx, address, true);
socketList.add(zmq);
} catch (Exception ex) {
// log error
}
}
return socketList;
}
// this method will be called by multiple threads to get the next live socket
public Optional<SocketHolder> getNextSocket() {
Optional<SocketHolder> liveSocket = Optional.absent();
List<Datacenters> dcs = Datacenters.getOrderedDatacenters();
for (Datacenters dc : dcs) {
liveSocket = getLiveSocket(liveSocketsByDatacenter.get(dc));
if (liveSocket.isPresent()) {
break;
}
}
return liveSocket;
}
private Optional<SocketHolder> getLiveSocket(final List<SocketHolder> listOfEndPoints) {
if (!CollectionUtils.isEmpty(listOfEndPoints)) {
Collections.shuffle(listOfEndPoints);
for (SocketHolder obj : listOfEndPoints) {
if (obj.isLive()) {
return Optional.of(obj);
}
}
}
return Optional.absent();
}
private void updateLiveSockets() {
Map<Datacenters, ImmutableList<String>> socketsByDatacenter = Utils.SERVERS;
for (Entry<Datacenters, ImmutableList<String>> entry : socketsByDatacenter.entrySet()) {
List<SocketHolder> liveSockets = liveSocketsByDatacenter.get(entry.getKey());
List<SocketHolder> liveUpdatedSockets = new ArrayList<>();
for (SocketHolder liveSocket : liveSockets) {
Socket socket = liveSocket.getSocket();
String endpoint = liveSocket.getEndpoint();
Map<byte[], byte[]> holder = populateMap();
boolean status = SendToSocket.getInstance().execute(3, holder, socket);
boolean isLive = (status) ? true : false;
SocketHolder zmq = new SocketHolder(socket, liveSocket.getContext(), endpoint, isLive);
liveUpdatedSockets.add(zmq);
}
liveSocketsByDatacenter.put(entry.getKey(), liveUpdatedSockets);
}
}
}
당신이 내 위 클래스에서 볼 수 있듯이 :
- 를 30 초마다 실행되는 단일 백그라운드 스레드에서, 나는 모든 라이브 소켓
liveSocketsByDatacenter
지도를 채 웁니다. - 그리고 여러 스레드에서
getNextSocket
메서드를 호출하여liveSocketsByDatacenter
맵을 사용하여 필요한 정보를 얻을 수있는 라이브 소켓을 제공합니다.
내 위 코드가 안전하며 모든 판독기 스레드에 liveSocketsByDatacenter
이 정확하게 표시됩니까? 단일 배경 스레드에서 매 30 초마다 liveSocketsByDatacenter
맵을 수정하고 나서 많은 판독기 스레드에서 수정하고 있으므로 getNextSocket
메서드를 호출하므로 여기에 잘못된 것이 있는지 확실하지 않습니다.
내 "getLiveSocket"메소드에 스레드 안전성 문제가있는 것처럼 보일 때마다 모든 읽기가 공유 된 ArrayList
을 맵 밖으로 가져 와서 셔플 한 것처럼 보입니까? 그리고 제가 그리워 할 수도있는 장소가 몇 개 더 없을 수도 있습니다. 내 코드에서 이러한 스레드 안전 문제를 해결하는 가장 좋은 방법은 무엇입니까?
더 좋은 방법이 있다면이 내용을 다시 작성하면됩니다.
좋은 제안. 나는 모든 것을 이해했다. 앞서 언급 한 것처럼 동기화 된 블록을 제거 할 수 있도록 AtomicReference를 사용하기로 결정했습니다. 나는 당신의 제안에 대한 모든 것을 올바르게 파악할 수 있도록 요지를 둡니다. (https://gist.github.com/TechGeeky/ae9f3b8863710a67c1d68a21c74c24da) 이제 우리는'AtomicReference'를 사용하고 있기 때문에, 많은 장소에서'AtomicReference'에'get()'메소드를 사용하여 실제 Map을 얻을 필요가 있습니다. 나는 그 것들을 나의 요지에서 편집했다. 내가 모든게 옳다는 것을 알려줘. –
@ user5447339 덮어 쓰지 않는 지점 # 3을 제외하고는 좋은 소리가납니다. –
죄송합니다. 나는 어떻게 든 그것을 놓쳤습니다. 다시 요점을 다시 편집했습니다.지도가 휘발성 인 경우 왜 동기화 된 블록이 필요하지 않은지 설명 할 수 있습니까? 또한'ConcurrentHashMap'을 사용하지 않는 것의 이점은 불변 버전에서'HashMap'을 사용하면 성능 이점을 얻을 수 있기 때문입니다. 내 이해를 위해서 왜 이것이 더 유익한 지 설명 할 수 있겠습니까? –