2013-02-09 5 views
3

Я пишу код всплеска, который не дает мне результатов, которые я ожидаю.java.sql.Connection Уровень изоляции

У меня есть таблица, которая в основном представляет собой строки счетчиков. Другие таблицы используют эти строки для генерации уникальных идентификаторов. Когда я запускаю код ниже того, что я исключал, это то, что первый поток, который достигнет оператора select, получит блокировку в этой строке или таблице, останавливая все чтение или запись по уникальному значению id. Однако второй поток всегда завершается до первого, из-за того, что он был усыпан в течение 1 секунды, поэтому они оба читают одно и то же значение и записывают одно и то же значение, поэтому он только увеличивается один раз и не дважды, как я исключал.

Есть ли что-то не так с моим кодом, или мое понимание уровня изоляции неверно?

Я снял код плиты котла. Стандартное sql.Connection с использованием базы данных MySQL.

private void incrementValue() { 

     connection 
       .setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE); 

     statement = connection.createStatement(); 

     System.out.println(Thread.currentThread().getName() 
       + " doing select"); 
     resultSet = statement.executeQuery("select * from counter"); 
     System.out.println(Thread.currentThread().getName() 
       + "after select"); 
     if (counter++ == 0) { 
      Thread.sleep(1000); 
     } 
     String incrementedValue = getIncrementedValue(resultSet); 

     statement.executeUpdate("update counter set counter='" 
       + incrementedValue + "'"); 


} 

private String getIncrementedValue(ResultSet resultSet) throws SQLException { 
    String value = ""; 
    if (resultSet.next()) { 
     System.out.println(Thread.currentThread().getName() + "Value was " 
       + resultSet.getString(1)); 

     value = (new Integer(resultSet.getString(1)) + 1) + ""; 

    } 

    return value; 

} 

Это называется от главного

public static void main(String[] args) { 
    DatabaseExample databaseExample = new DatabaseExample(); 

    Runnable runnable = new Runnable() { 

     @Override 
     public void run() { 
      DatabaseExample databaseExample = new DatabaseExample(); 
      databaseExample.incrementValue(); 
     } 
    }; 
    new Thread(runnable).start(); 

    databaseExample.incrementValue(); 
} 
+0

В этом случае замок должен быть выполнен с применением уровня. Просто синхронизируйте свой метод, используя ключевое слово 'synchronized'. Обратите внимание, что это повлияет на производительность приложения. –

ответ

2

Даже в SERIALIZABLE уровне изоляции, несколько выбирает могут быть выполнены параллельно. Если вы хотите поместить блокировку в строку в предложении select, используйте select ... for update.

Ссылка:

http://dev.mysql.com/doc/refman/5.1/en/select.html:

При использовании FOR UPDATE с механизмом хранения, который использует страницу или блокировки строк, строки, изученные запрос от записи блокированного до конца текущей транзакции , Использование LOCK IN SHARE MODE устанавливает общую блокировку, позволяющую другим транзакциям считывать проверенные строки, но не обновлять или удалять их.

http://dev.mysql.com/doc/refman/5.1/en/set-transaction.html#isolevel_serializable:

SERIALIZABLE

Этот уровень, как REPEATABLE READ, но InnoDB неявно преобразует всю равнину ЗЕЬЕСТА в SELECT ... LOCK IN SHARE MODE, если автокоммят отключено.

+0

JB Nizet: Спасибо за ответ. Я надеялся на решение, не относящееся к базе данных, так как это нужно будет запускать в базах данных у разных поставщиков, но, похоже, их нет. FOR UPDATE, похоже, работает на MySQL и Oracle и на SQL Server, используя WITH (UPDLOCK), в то время как ловить исключение тупика и повторно запускать код. – Medu

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