Доступ к глобальной переменной в многопоточном сервере Tomcat - PullRequest
2 голосов
/ 03 мая 2010

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

У меня есть одноэлементный объект, который я создаю следующим образом:

private static volatile KeyMapper mapper = null;

public static KeyMapper getMapper()
{
    if(mapper == null)
    {
        synchronized(Utils.class)
        {
            if(mapper == null)
            {
                mapper = new LocalMemoryMapper();
            }
        }
    }

    return mapper;
}

Класс KeyMapper по сути является синхронизированной оболочкой для HashMap, имеющей только две функции: одну для добавления сопоставления и одну для удаления сопоставления. При работе в Tomcat 6.24 на моей 32-битной Windows-машине все работает нормально. Однако при работе на 64-битной машине Linux (CentOS 5.4 с OpenJDK 1.6.0-b09) я добавляю одно сопоставление и распечатываю размер HashMap, используемого KeyMapper для проверки того, что сопоставление добавлено (т. Е. Verify size = 1). Затем я пытаюсь получить сопоставление с помощью другого запроса, и я получаю нулевое значение, и когда я проверял размер HashMap, он был равен 0. Я уверен, что сопоставление не было случайно удалено, так как я закомментировал все вызовы для удаления (и я не использую чистые или любые другие мутаторы, просто получи и положи).

Запросы проходят через Tomcat 6.24 (настроен на использование 200 потоков с минимум 4 потоками), и я передал -Xnoclassgc в jvm, чтобы убедиться, что класс случайно не получает сборщик мусора (jvm также работает в -server Режим). Я также добавил метод финализации в KeyMapper для печати в stderr, если он когда-либо собирает мусор, чтобы убедиться, что он не был мусором.

Я в своем уме и не могу понять, почему в одну минуту есть запись в HashMap, а в следующую нет: (

Ответы [ 5 ]

5 голосов
/ 03 мая 2010

Еще одна дикая догадка: возможно ли, что два запроса обслуживаются разными копиями вашего веб-приложения? Каждый будет в своем собственном ClassLoader и, следовательно, будет иметь другую копию синглтона.

1 голос
/ 03 мая 2010

С этим решением JVM гарантирует, что это только один маппер, и он инициализируется перед использованием.

public enum KeyMapperFactory {

    ;

    private static KeyMapper mapper = new LocalMemoryMapper();  

    public static KeyMapper getMapper() {
        return mapper;
    }
}
1 голос
/ 03 мая 2010

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

http://en.wikipedia.org/wiki/Double-checked_locking#Usage_in_Java

1 голос
/ 03 мая 2010

Вы пытались снять внешний чек

if(mapper == null)
{

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

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

0 голосов
/ 03 мая 2010

Я нашел довольно плохое решение. Я экспортировал свой код в виде JAR и поместил его в $ TOMCAT / lib, и это сработало. Это явно проблема загрузчика классов.

Редактировать: разобрался с решением

Хорошо, я наконец-то понял проблему.

Я сделал свое приложение приложением по умолчанию для сервера, добавив в server.xml и указав путь к «». Однако, когда я получал к нему доступ через URL http://localhost/somepage.jsp для некоторых вещей, но также и для URL http://localhost/appname/anotherpage.jsp для других вещей.

После того как я изменил все URL-адреса, чтобы использовать http://localhost/ вместо http://localhost/appname, проблема была исправлена.

...