【问题标题】:ConcurrentModificationException for a Java WebSocket serverJava WebSocket 服务器的 ConcurrentModificationException
【发布时间】:2021-08-29 09:55:07
【问题描述】:

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

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 组件的样子:


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 秒,但一旦我开始绘制更多形状,套接字服务器就会崩溃,ConcurrentModificationExceptionfor (Session peer : peers) 在线。

如果有任何帮助,我将使用 Java 11 和 Tomcat 10 作为服务器。

【问题讨论】:

    标签: java tomcat websocket


    【解决方案1】:

    Collections.synchronizedSet 创建一个单项访问同步的集合。这涉及诸如获取、添加、删除等方法。 但是,迭代器不同步。当另一个线程在迭代过程中修改集合时,您将获得 ConcurrentModificationException。

    您有两种解决方案。使用这样的同步块保护外观:

    synchronized(peers) {
      for (Session peer: peers) {
        ...
      }
    }
    

    或者,更好的是,切换到 ConcurrentHashSet,它保证了多个线程的优化访问,而不会抛出 ConcurrentModificationException。

    【讨论】:

      【解决方案2】:

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

      synchronized(peers){
              for (Session peer : peers) {
                  if (!peer.equals(session)) {
                      peer.getAsyncRemote().sendObject(drawing);
                  }
              }   
          }
      

      这里是documentation

      【讨论】:

      • 感谢您的回答。它修复了抛出的异常,但在绘制一条简单的线后服务器仍然停止工作。不好的是也没有异常消息。在浏览器中它只显示WebSocket is already in CLOSING or CLOSED state。有什么想法吗?
      • 对不起,我不这样做,因为这看起来更像是套接字实现问题,而不是 ConcurrentModification 问题
      猜你喜欢
      • 1970-01-01
      • 2021-04-11
      • 1970-01-01
      • 1970-01-01
      • 2020-07-04
      • 1970-01-01
      • 1970-01-01
      • 2011-08-13
      • 1970-01-01
      相关资源
      最近更新 更多