如何使这个线程安全?

时间:2012-04-01 19:21:31

标签: java multithreading asynchronous thread-safety

我有一台服务器从客户端接收各种xml消息(每个客户端一个线程),并根据消息类型将消息路由到不同的函数。例如。如果消息中的第一个元素包含字符串'login',则表示这是一个登录消息,因此将消息路由到login()函数。

无论如何,我想发布此消息,以便在连接多个客户端并且调度程序在消息路由中间切换线程时,事情不会搞砸。所以这就是我如何路由消息 -

public void processMessagesFromClient(Client client)
{
    Document message;

    while (true)
    {
        try
        {
            message = client.inputStream.readObject();

            /*
             * Determine the message type
             */
            String messageType = getMessageType(message);

            // Route the message depending on its type
            switch (messageType)
            {
                case LOGIN:
                    userModel.handleLogin();
                ...
                ...
                ...
                etc...
             }
        } catch(Exception e) {}
   }

那么我怎样才能使这个线程安全?我想我需要在某个地方放一个同步语句,但我不知道在哪里。我也一直在阅读这个主题,我发现这篇文章说在'this'上使用同步有一个问题 - https://stackoverflow.com/a/416198/1088617

此处的另一篇文章称单例不适合使用同步(上面代码中的我的类是单例) - https://stackoverflow.com/a/416202/1088617

7 个答案:

答案 0 :(得分:2)

我实际上有一个消息处理程序线程,它负责读取传入的消息。然后,这将处理切换到工作线程以执行消息的耗时处理。您可以使用Java ThreadPoolExecutor来管理它。

答案 1 :(得分:2)

您的类已经线程安全,因为您只使用局部变量。 线程安全仅在您访问类 state (即字段)时才会发挥作用,而您的代码似乎没有(似乎)这样做。

您所谈论的是序列化 - 您希望通过一个点汇集所有消息处理,以保证消息处理是一次一个(开始和完成原子< / em>的)。解决方案很简单:使用static synchronized方法:

public void processMessagesFromClient(Client client) {
    Document Message;

    while (true) {
        processMessage(client);
    }
}

private static synchronized processMessage(Client client) {
    try {
        message = client.inputStream.readObject();

        String messageType = getMessageType(message);

        // Route the message depending on its type
        switch (messageType) {
            case LOGIN:
                userModel.handleLogin();
            ...
            etc...
        }
    } catch(Exception e) {}
}

FYI static synchronized方法使用Class对象作为锁。此代码将使您的代码行为像单个线程,您的问题似乎需要。

答案 2 :(得分:1)

如果每个线程都有这些对象之一,则没有问题。您只需要同步一个可由其中一个线程修改的共享对象。

public void processMessagesFromClient(Client client) {    
    while (true) {
        processMessage(client);
    }
}

private void processMessage(Client client) {
    try {
        Document message = client.inputStream.readObject();

        String messageType = getMessageType(message);

        // Route the message depending on its type
        switch (messageType) {
            case LOGIN:
                userModel.handleLogin();
            ...
            etc...
        }
    } catch(Exception e) {}
}

答案 3 :(得分:1)

如果每个连接已有1个线程,则必须同步的唯一事情是处理事件的函数(即userModel.handleLogin()等函数)。

答案 4 :(得分:1)

我想最好的解决方案应该是使用一个类似ConcurrentQueue的线程安全队列,并使用一个工作线程来获取这些值并逐个运行这些操作。

答案 5 :(得分:0)

您需要知道哪个资源应该只在某个时间使用一个线程。

在您的情况下,阅读下一条消息可能需要受到保护。

 synchronize (lock) {
      message = client.inputStream.readObject();
 }

但是,您的代码示例并未真正显示需要防止并发访问的内容

答案 6 :(得分:0)

该方法本身是线程安全的。 但是,请注意您的类是单身,您可能希望在double checked locking中使用getInstance来确保线程安全。 此外,您应确保将您的实例设置为static

      class Foo {
            private static volatile Foo instance = null;
            public static Foo getInstance() {
                if (instance == null) 
                {
                    synchronized(this) 
                    {
                        if (instance == null)
                            instance = new Foo ();
                    }
                }
                return instance ;
            }
        }