![](/img/trans.png)
[英]Is it possible that I get ConcurrentModificationException when all the methods in my program are synchronized?
[英]ConcurrentModificationException on synchronized() code - how is it possible?
我有一個以下 spring bean(稍微編輯)這個 bean 負責添加/刪除 websocket 連接到內部連接(this.sockets)。 每次需要發送消息時,都可以添加/刪除新的連接並對其進行迭代。
正如您所看到的, sockets
字段上的每個操作都包含在一個synchronized()
塊中,它是私有的,不會在這個 class 之外使用,這個字段的每次使用都在我粘貼在下面的代碼中。
但我仍然不時收到在onApplicationEvent
調用的 for 循環中發生的ConcurrentModificartionException
。 這個調用可能確實被應用程序中的多個線程調用,但我的理解是,如果每個實際修改或迭代this.socket
變量的調用都包裝在同一個 object 上的 synchronized() 塊中,那么這些調用中的每一個都不能在並行並且必須相互“等待”。
這段代碼怎么可能拋出ConcurrentModificationException
? 請記住,我正在嘗試了解出現異常的原因,並且我並不是在尋找一個快速解決方案,它只會解決這個問題 go(所以像“你為什么不在這里使用 XXX”這樣的回答實際上並沒有解決任何問題我 - 我試圖找出我在代碼中做錯了什么)。
代碼:
public class MyClass extends TextWebSocketHandler implements ApplicationListener<AppEventMessage>{
private static final Logger log= LoggerFactory.getLogger(MyClass.class);
private Set<WebSocketSession> sockets=new HashSet<>();
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
super.afterConnectionEstablished(session);
synchronized (this.sockets) {
this.sockets.add(session);
}
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
synchronized (this.sockets) {
this.sockets.remove(session);
}
}
@Override
public void onApplicationEvent(AppEventMessage event) {
synchronized (this.sockets) {
for (WebSocketSession session : this.sockets) {
try {
log.info("sendin");
session.sendMessage(new TextMessage(event.getMessage()));
} catch (Throwable e) {
log.warn("error");
}
}
}
}
}
stacktrace 最相關的部分(附上我的評論)
java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.nextNode(HashMap.java:1445)
at java.util.HashMap$KeyIterator.next(HashMap.java:1469)
at ...MyClass.onApplicationEvent(ApplicationEventWebsocketHandler.java:46) <- (line 46 is the for(WebSocketSession ...) line)
at ...MyClass.onApplicationEvent(ApplicationEventWebsocketHandler.java:19) <- (line 19 is the "public class Myclass" line - how it is possible?)
at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:163)
目前同意你的診斷。 還有第二種方法可以獲取通常被忽視的ConcurrentModificationException
......
這些synchronized
鎖是可重入的,這意味着在一個方法中持有鎖的線程可以訪問所有方法。 因此,導致 CME 的線程很可能是for
循環中的回調,最終返回到連接偵聽器方法中。
IMO,最可能的情況是連接僅在嘗試sendMessage
時才被檢測為已關閉。
解決方案:有更聰明的解決方案,但這是一個不會 go 錯誤的解決方案:
// Take copy of sockets before iterating it to defend against self-induced ConcurrentModificationException
for (WebSocketSession session : new HashSet<>(this.sockets)) {
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.