Как я могу сделать эту тему безопасной? - PullRequest
1 голос
/ 01 апреля 2012

У меня есть сервер, который получает различные 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 ]

2 голосов
/ 01 апреля 2012

Ваш класс уже потокобезопасен, потому что вы используете только локальные переменные. Безопасность потоков вступает в игру только тогда, когда вы обращаетесь к классу state (т.е. к полям), чего ваш код (кажется) не делает.

То, о чем вы говорите, это сериализация - вы хотите направить всю обработку сообщений через одну точку, чтобы гарантировать, что обработка сообщений выполняется по одному (запускается и заканчивается атомарно ). Решение простое: используйте метод 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 голосов
/ 01 апреля 2012

Я бы на самом деле имел поток обработчика сообщений, который отвечает за чтение входящих сообщений.Затем он передаст обработку рабочему потоку, чтобы выполнить обработку сообщения, отнимающую много времени.Вы можете использовать Java ThreadPoolExecutor для управления этим.

1 голос
/ 01 апреля 2012

Полагаю, лучшим решением должно быть использование потоковой очереди, такой как ConcurrentQueue, и использование одного рабочего потока, чтобы получить эти значения и выполнить действия одно за другим.

1 голос
/ 01 апреля 2012

Если у вас уже есть 1 поток на соединение, единственное, что вам нужно синхронизировать, - это функции, которые обрабатывают события (то есть такие функции, как userModel.handleLogin()).

1 голос
/ 01 апреля 2012

Если у вас есть один из этих объектов на поток, у вас нет проблем. Вам нужно только синхронизировать общий объект, который может быть изменен одним из потоков.

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) {}
}
0 голосов
/ 02 апреля 2012

Сам метод является поточно-ориентированным.Однако, отметив, что этот ваш класс является синглтоном, вы можете использовать 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 ;
            }
        }
0 голосов
/ 01 апреля 2012

Вы должны знать, какой ресурс должен использоваться только одним потоком в определенное время.

В вашем случае вполне вероятно, что чтение следующего сообщения должно быть защищено.

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

Тем не менее, ваш пример кода не показывает, что нужно защищать от одновременного доступа

...