首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Java WebSocket服务器的ConcurrentModificationException

Java WebSocket服务器的ConcurrentModificationException
EN

Stack Overflow用户
提问于 2021-08-29 09:55:07
回答 2查看 69关注 0票数 0

我正在构建一个处理对象绘制的WebSocket服务器。下面是服务器的类的样子:

代码语言:javascript
复制
import fmi.whiteboard.models.paths.*;
import jakarta.websocket.*;
import jakarta.websocket.server.ServerEndpoint;

import java.io.IOException;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

@ServerEndpoint(value = "/whiteboard",
        encoders = {DrawingEncoder.class, ShapeEncoder.class, PathEncoder.class},
        decoders = {DrawingDecoder.class, ShapeDecoder.class, PathDecoder.class})
public class WhiteboardServer {

    private static Set<Session> peers = Collections.synchronizedSet(new HashSet<Session>());

    @OnOpen
    public void onOpen(Session peer) {
        peers.add(peer);
    }

    @OnClose
    public void onClose(Session peer) {
        peers.remove(peer);
    }

    @OnMessage
    public void broadcastShape(Drawing drawing, Session session) throws IOException, EncodeException {
        for (Session peer : peers) {
            if (!peer.equals(session)) {
                peer.getAsyncRemote().sendObject(drawing);
            }
        }
    }

}

下面是处理绘图的ReactJS组件的外观:

代码语言:javascript
复制
export default function Whiteboard() {
  const canvas = useRef(null);

  const ws = useMemo(() => {
    const socket = new WebSocket("ws://localhost:8080/backend_war/whiteboard");
    socket.binaryType = "arraybuffer";
    return socket;
  }, []);

  useEffect(() => {
    ws.onmessage = (evt: MessageEvent) => {
      const data: SocketResponse = JSON.parse(evt.data);
      if (data && data.shapes) {
        canvas?.current?.loadPaths(data.shapes);
      }
    };

    ws.onerror= (err) => console.log(err);
    
  }, [ws]);

  const onDraw = (message: CanvasPath[]) => {
    ws.send(JSON.stringify({ shapes: message }));
  };

  return (
    <>
      <ReactSketchCanvas
        ref={canvas}
        style={styles}
        onUpdate={(paths) => setPaths(paths)}
        strokeWidth={4}
        strokeColor="red"
      />
    </>
  );
}

这个应用程序在2-3秒内运行良好,但当我开始绘制更多的形状时,socket服务器在与for (Session peer : peers)的线路上使用ConcurrentModificationException崩溃。

如果有帮助的话,我使用Java11和Tomcat10作为服务器。

EN

回答 2

Stack Overflow用户

发布于 2021-08-29 13:24:31

Collections.synchronizedSet创建了一个同步单个项目访问的集合。这涉及到get、add、remove等方法。然而,迭代器并不同步。在迭代过程中,当另一个线程修改集合时,您将获得ConcurrentModificationException。

您有两个解决方案。或者像这样使用同步块来保护外观:

代码语言:javascript
复制
synchronized(peers) {
  for (Session peer: peers) {
    ...
  }
}

或者,更好的方法是切换到ConcurrentHashSet,它可以保证在不抛出ConcurrentModificationException的情况下实现多线程的优化访问。

票数 1
EN

Stack Overflow用户

发布于 2021-08-29 10:18:31

当您使用Collections.synchronizedSet同步和现有集合时,它只同步访问数据的方法,如get()set()...而不是迭代器。因此,您还需要像这样同步迭代器,例如

代码语言:javascript
复制
synchronized(peers){
        for (Session peer : peers) {
            if (!peer.equals(session)) {
                peer.getAsyncRemote().sendObject(drawing);
            }
        }   
    }

这是documentation

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/68971764

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档