0

В одном из наших унаследованных приложений произошла утечка базы данных, и я отследил ее до этого маленького драгоценного камня. От отладки я могу видеть, что такое же логическое соединение возвращается для нескольких потоков (не хорошо!). Но я пытаюсь понять, почему это происходит.Укажите проблему параллелизма

Мы используем драйвер ojdbc6, настроенный на источник данных WebLogic с пулом соединений.

Код, который производит проблему

public class MyDummyDaoUtil { 

    //note: this is a public field in a singleton (not a static field though...) 
    public Connection conn; 

    private MyDummyDaoUtil() { 
    } 

    public static MyDummyDaoUtil getInstance() { 
     if (instance == null) { 
      instance = new MyDummyDaoUtil(); 
     } 

     return instance; 
    } 

    private DataSource getDataSource(final String dsName) 
     throws NamingException { 
     return ServiceLocator.getInstance().getDataSource(dsName); 
    } 

    public static Connection getConnection(final String source) 
     throws NamingException { 
     return MyDummyDaoUtil.getInstance().getDBConnection(source); 
    } 

    private Connection getDBConnection(final String source) 
     throws NamingException { 

     //the same logical connection is produced by the data source or something else happening? 
     conn = getDataSource(source).getConnection(); 

     conn.setAutoCommit(false); 

     return conn; 
    } 
} 

Обновлено FIX

public class MyDummyDaoUtil { 

    private MyDummyDaoUtil() { 
    } 

    public static MyDummyDaoUtil getInstance() { 
     if (instance == null) { 
      instance = new MyDummyDaoUtil(); 
     } 

     return instance; 
    } 

    private DataSource getDataSource(final String dsName) 
     throws NamingException { 
     return ServiceLocator.getInstance().getDataSource(dsName); 
    } 

    public static Connection getConnection(final String source) 
     throws NamingException { 
     return MyDummyDaoUtil.getInstance().getDBConnection(source); 
    } 

    private Connection getDBConnection(final String source) 
     throws NamingException { 

     Connection conn = getDataSource(source).getConnection(); 
     conn.setAutoCommit(false); 

     return conn; 
    } 
} 

Резюме затруднительного

  1. Некорректное ленивым initializa Тион экземпляра
  2. Подключение должно быть локальной переменной в методе, а не класс синглтон
+0

Вы говорите, что MyDummyDaoUtil - синглтон. Если это общий доступ к нескольким потокам, тогда будет возможность запросить два соединения, которые могут быть изменены с использованием ссылки и той же ссылки (последней), которая была возвращена из обоих вызовов в getDBConnection. Соединение, которое не было возвращено, никогда не может быть закрыто. Был ли этот код не исчерпывать доступные соединения? – Yoztastic

+0

@Yoztastic Связи, вероятно, были сбиты их финализатором, когда они были собраны мусором, скрывая эту проблему. – Durandal

+0

@Yoztastic - Да, это был первоначальный симптом. Наши связи были исчерпаны, и нам пришлось включить неактивный таймаут, чтобы помочь восстановить эти утеченные соединения, пока мы отслеживаем проблему. Из того, что я могу сказать, код был реализован таким образом с первого дня (что страшно ...) Непонятно, как эта проблема не отображалась в нашей старой системе. Я предполагаю, что связи были восстановлены как-то, и мы не понимали, что эта проблема существует. – user1766760

ответ

0

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

Попробуйте какой-то один вариант:

  • использование getInstance() синхронизированный метод

    public static synchronized MyDummyDaoUtil getInstance() { 
        if (instance == null) { 
         instance = new MyDummyDaoUtil(); 
        } 
        return instance; 
    } 
    
  • создать его экземпляр охотно

    private static MyDummyDaoUtil instance = new MyDummyDaoUtil(); 
    public static MyDummyDaoUtil getInstance() { 
        return instance; 
    } 
    
  • использование Doub Проверка ле блокировки механизм

    public static MyDummyDaoUtil getInstance() { 
    
        if (instance == null) { 
         synchronized(MyDummyDaoUtil.class){ 
          if (instance == null) { 
           instance = new MyDummyDaoUtil(); 
          } 
         } 
        } 
        return instance; 
    } 
    

Примечание: Не забудьте закрыть соединение один его сделали.

Read more...

+0

хороший момент, я пропустил неправильный ленивый init экземпляра – user1766760

0

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

У вас есть изменяемое состояние (MyDummyDaoUtil.conn), который вы используете. Рассмотрим следующий сценарий - две нити (А и В) оба призывающих оригинальную функцию, в то же время:

private Connection getDBConnection(final String source) 
    throws NamingException { 
    conn = getDataSource(source).getConnection(); //line 1 
    conn.setAutoCommit(false);     //line 2 
    return conn;         //line 3 
} 

Есть много возможных последовательностей здесь, но вот пример проблематичной один:

  • Резьба A выполняет линию 1.Ваш источник данных возвращает новое соединение (назовем его connectionA), а MyDummyDaoUtil.conn - connectionA.
  • Thread B выполняет строку 1. Ваш источник данных возвращает новое соединение (назовем это connectionB), а MyDummyDaoUtil.conn - connectionB.
  • Резьба A выполняет линию 2. conn теперь connectionB, поэтому это приводит к установке auto commit в false на connectionB.
  • Резьба A выполняет линию 3, возвращая connectionB (это тот, который создан из другого потока).
  • Thread B выполняется строка 2, установка автоматической фиксации в ложна на connectionB (который является не-оп, потому что Поток А уже сделал это случайно)
  • Thread B выполняется строка 3, возвращая connectionB.

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

Смежные вопросы