Несколько потоков, вызывающих статический вспомогательный метод - PullRequest
9 голосов
/ 01 июня 2011

У меня есть веб-приложение, работающее на Tomcat.

Существует несколько вычислений, которые необходимо выполнить в нескольких местах веб-приложения.Могу ли я сделать эти вычисления статическими вспомогательными функциями?Если на сервере достаточно процессорных ядер, могут ли несколько вызовов этой статической функции (в результате нескольких запросов к различным сервлетам) выполняться параллельно?Или один запрос должен ждать, пока другой запрос завершит вызов?

public class Helper {
    public static void doSomething(int arg1, int arg2) {
        // do something with the args
        return val;
    }
}

, если вызовы выполняются параллельно: у меня есть другой вспомогательный класс со статическими функциями, но этот класс содержит закрытый статический член, который используетсяв статических функциях.Как я могу убедиться, что функции являются поточно-ориентированными?

public class Helper {

    private static SomeObject obj;

    public static void changeMember() {
        Helper.obj.changeValue();
    }

    public static String readMember() {
        Helper.obj.readValue();
    }

}

changeValue() и readValue() читают / изменяют одну и ту же переменную-член Helper.obj.Нужно ли синхронизировать все статические функции или только блок, в котором используется Helper.obj?Если я должен использовать блок, какой объект я должен использовать, чтобы заблокировать его?

Ответы [ 7 ]

5 голосов
/ 01 июня 2011

Могу ли я сделать эти вычисления статическими вспомогательными функциями?если на сервере достаточно процессорных ядер, могут ли несколько вызовов этой статической функции (в результате нескольких запросов к различным сервлетам) выполняться параллельно?

Да и да.

Должен ли я синхронизировать все статические функции

Это сработает.

или просто блок, где используется Helper.obj

Это также будет работать.

Если я должен использовать блок, какой объект я должен использовать для его блокировки?

Использовать static Object:

public class Helper {

    private static SomeObject obj;
    private static final Object mutex = new Object();

    public static void changeMember() {
        synchronized (mutex) {
            obj.changeValue();
        }
    }

    public static String readMember() {
        synchronized (mutex) {
            obj.readValue();
        }
    }
}

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

5 голосов
/ 01 июня 2011

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

Обратите внимание, что есть шаблоны, которые помогут вам в этом. Вы можете использовать шаблон стратегии (в его канонической форме стратегия должна быть выбрана во время выполнения, которая может или не может применяться здесь), или вариант. Просто создайте класс для каждого вычисления с помощью метода execute (и интерфейса, который имеет метод) и передайте объект контекста для выполнения. Контекст содержит все состояние расчета. Один экземпляр стратегии на поток, с его контекстом, и у вас не должно быть проблем.

4 голосов
/ 01 июня 2011

Если вам не нужно делиться им, вы можете сделать его локальным, поэтому он не должен быть безопасным.

public class Helper {

private static final ThreadLocal<SomeObject> obj = new ThreadLocal<SomeObject>() {
    public SomeObject initialValue() {
        return enw SomeObject();
    }
}

public static void changeMember() {
    Helper.obj.get().changeValue();
}

public static String readMember() {
    Helper.obj.get().readValue();
}

}
2 голосов
/ 01 июня 2011

Я подведу итог тому, что было сказано в комментариях к ответу Мэтта Болла, так как в конце оно получилось довольно длинным, а сообщение потеряно: и сообщение было

в общей среде.Как и веб-сервер / сервер приложений, вы должны очень стараться найти решение без синхронизации.Использование статических помощников, синхронизированных со статическим объектом, может работать достаточно хорошо для автономного приложения с одним пользователем перед экраном, в многопользовательском / многопользовательском сценарии это, скорее всего, приведет к очень низкой производительности - это фактически означает сериализацию доступа кВаше приложение, все пользователи должны будут ждать на одной и той же блокировки.Вы можете не заметить проблему в течение длительного времени: если вычисления выполняются достаточно быстро и нагрузка распределяется равномерно.

Но вдруг все ваши пользователи могут попытаться выполнить вычисления в 9:00, и ваше приложениеперестану работать!Я имею в виду, что на самом деле не останавливаться, но все они блокировали бы блокировку и создавали огромную очередь.

Теперь, независимо от необходимости общего состояния, так как вы изначально назвали вычисления субъектом синхронизации: нужно ли их результатыбыть общим?Или эти расчеты специфичны для пользователя / сеанса?В последнем случае будет достаточно ThreadLocal в соответствии с Peter Lawrey.В противном случае я бы сказал, что для общей производительности было бы лучше дублировать вычисления для всех, кто в них нуждается, чтобы не синхронизировать (зависит от стоимости).

Управление сессиями также лучше оставить в контейнере: онобыл оптимизирован для их эффективной обработки, при необходимости, включая кластеризацию и т. д. Я сомневаюсь, что можно было бы найти лучшее решение, не вкладывая много работы и не делая много ошибок на пути туда.Но, как заявил Мэтт Болл, его лучше спросить отдельно.

1 голос
/ 01 июня 2011

Для первой части: Да, эти вызовы независимы и выполняются параллельно при вызове разными потоками.

Для последней части: Используйте блоки синхронизации на параллельном объекте, фиктивном объекте или объекте класса. Помните о каскадных блоках синхронизации. Они могут привести к мертвым замкам, если приобретены в другом порядке.

1 голос
/ 01 июня 2011

В первом случае вам не нужно беспокоиться о проблемах с многопоточностью, поскольку переменные являются локальными для каждого потока.Вы правильно идентифицируете проблему во втором случае, потому что несколько потоков будут читать / записывать один и тот же объект.Синхронизация по методам будет работать так же, как и синхронизированные блоки.

1 голос
/ 01 июня 2011

Если вас беспокоит синхронизация и безопасность потоков, не используйте статические помощники. Создайте нормальный класс с вашими вспомогательными методами и создайте экземпляр по запросу сервлета. Будьте проще: -)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...