java - 多线程Java服务器: allowing one thread to access another one

标签 java multithreading oop coding-style

希望代码本身可以解释这个问题:

class Server {

    public void main() {
        // ...
        ServerSocket serverSocket = new ServerSocket(PORT);
        while (true) {
            Socket socket = serverSocket.accept();
            Thread thread = new Thread(new Session(socket));
            thread.start();
        }
        // ..
    }

    public static synchronized Session findByUser(String user) {
        for (int i = 0; i < sessions.size(); i++) {
            Session session = sessions.get(i);
            if (session.getUserID().equals(user)) {
                return session;
            }
        }
        return null;
    }

}

class Session {
    public Session(Socket socket) {
        attach(socket);
    }

    public void attach(Socket socket) {
        // get socket's input and output streams
        // start another thread to handle messaging (if not already started)
    }

    public void run() {
        // ...
        // user logs in and if he's got another session opened, attach to it
        Session session = Server.findByUser(userId);
        if (session != null) {
            // close input and output streams
            // ...
            session.attach(socket);
            return;
        }

        // ..

    }
}

我的问题是:在 Server.findByUser 方法中发布 session 引用是否安全,是否违反 OOP 风格等? 或者我应该通过一些不可变的 id 引用 session 并封装整个内容?您还想在这里更改什么吗?

String sessionId = Server.findByUser(userId);
if (sessionId != null && sessionId.length() > 0) {
    // close input and output streams
    // ...
    Server.attach(sessionId, socket);
    return;
}

托马斯:

感谢您的回答。

我同意,在现实世界中,在创建Session的新实例时使用依赖注入(inject)是个好主意。 ,但可能还有一个接口(interface),对吧(代码如下)?尽管我可能应该对此进行单元测试,但让我们考虑一下我没有。那么我只需要一个服务器实例。 使用静态方法而不是单例方法是否会构成巨大的面向对象犯罪?

interface Server {
    Session findByUser(String user);
}

class ServerImpl implements Server {
    public Session findByUser(String user) { }
}

class Session {
   public Session(Server server, Socket socket) { }
} 

关于 attach(...) 的好观点方法 - 我什至从未考虑过子类化 Session类,这可能就是为什么我没有想到在构造函数中调用公共(public)方法有多么危险。但是我实际上需要一些公共(public)方法将 session 附加到不同的套接字,所以也许是一对方法?

class Session {
    public Session(Socket socket) {
       attach_socket(socket);
    }

    public void attach(Socket socket) {
        attach_socket(socket);
    }

    private void attach_socket(Socket socket) {
        // ...
    }
}

确实允许Session的客户端调用attach(...)似乎不对。这可能是只有服务器才能访问的重要方法之一。如果没有 C++ 的友谊关系,我该怎么做呢?不知何故,我想到了内部类,但我没有考虑太多,所以这可能是一条完全错误的道路。

每次收到新连接时,我都会生成一个新线程(并创建与其关联的新 session 实例)来处理传输。这样,当用户发送登录命令时,服务器就准备好接受新连接。一旦用户的身份得到验证,我就会检查他是否尚未登录(有另一个正在进行的 session )。如果他是,那么我将正在进行的 session 与其套接字分离,关闭该套接字,将正在进行的 session 附加到当前套接字并关闭当前 session 。希望这能更清楚地解释实际发生的情况?也许这里使用“ session ”这个词有点不幸。我真正拥有的是为每个连接(和 3 个线程)创建 4 个不同的对象:套接字处理程序、消息发送者、消息接收者和 session (如果这是一个好的解决方案,那就是另一个问题了......)。我只是尝试简单地查看源代码来关注问题。

我完全同意,当您可以使用 map 时,迭代 session 列表是没有意义的。但我担心这可能是我正在编写的代码遇到的较小问题之一(相信我)。我应该提到它实际上是一些遗留系统,毫不奇怪,最近被发现存在一些并发和性能问题。我的任务是修复它...当您几乎只掌握多线程的理论知识或仅使用它来显示进度条时,这不是一件容易的任务。

如果经过这个相当冗长的澄清,您对架构有更多的见解,我非常愿意倾听。

最佳答案

您应该首先创建服务器类 OO(即非静态)并使用 dependency injection在 session 类中:

class Server {
    public Session findByUser(String user) { }
}

class Session{
   public Session(Server server, Socket socket){}
}

public void attach(..)必须是私有(private)的以确保封装和正确的初始化。子类可能会破坏 Session 类,否则如下所示:

class BadSession extends Session{

@Override public void attach(Socket socket) {
    //this is not initialized at this point

    //now the instance is broken        
  }
}

从客户端调用attach似乎也是无效的。

将套接字附加到 session 的责任应该是服务器的一部分。这是决定哪个 Session 获取哪个 Socket 的正确位置。据我了解您的代码,您正在使用套接字创建一个 session 。不知何故,你发现用户已经有一个 session (带有另一个套接字)。现在您将当前 session 附加到此套接字。现在有有两个 session 的旧套接字没有 session 的新套接字。我认为传统的 session 应该有多个套接字,而不是相反:

Session session = findSession(userId);
session.attach(socket);

class Session{
   List<Socket> sockets;
}

在此更改之后,线程将不会分配给 session ,而是分配给套接字处理程序,该处理程序处理一个套接字的输入流并相应地更改 session 。

对方法使用同步 public static synchronized Session findByUser(String user)不足以保证线程安全。您必须确保 session 的查找(按用户)和 session 的注册(如果用户未知)必须是原子。语义应类似于 putIfAbsent ConcurrentMap 的。 (无论如何,迭代 session 列表效率不高。您应该使用 Map<Id, Session> 。)

我希望这会有所帮助。

关于java - 多线程Java服务器: allowing one thread to access another one,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1807575/

相关文章:

java - 从 Firebase 存储中获取随机图像

java - EJB 将所有异常包装成 EJBException

multithreading - 如何检测和调试多线程问题?

Java在线程中更改字符串值

c# - 工厂与实例构造函数

java - java gui 上的背景图像

java - 在压力测试下,函数在同时调用时有不同的行为

python - 寻找组织大型 kivy 项目的好方法

c++ - const 引用字段作为 C++ 类中的只读属性

java - Android 获取 EditText 中的值