Question

I am writing some spike code that isn't giving me the the results I am expecting.

I have a table that is basically rows of counters. Other tables use these rows to generate what should be unique ID's. When I run the code below what I excepted is that the first thread to reach the the select statement would acquire a lock on that row, or table, stopping all read or writes on the unique id value. However the second thread always completes before the first one, due to it been put to sleep for 1s, thus they both read the same value and write the same value, so it's only increment once and not twice as I excepted.

Is there anything wrong with my code, or is my understanding of the Isolation level's incorrect?

I have removed the boiler plate code. Standard sql.Connection using a MySQL Database.

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;

}

This is called from main

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();
}
Was it helpful?

Solution

Even in the SERIALIZABLE isolation level, multiple selects can be made in parallel. If you want to put a lock on the row on the select clause, use select ... for update.

References:

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

If you use FOR UPDATE with a storage engine that uses page or row locks, rows examined by the query are write-locked until the end of the current transaction. Using LOCK IN SHARE MODE sets a shared lock that permits other transactions to read the examined rows but not to update or delete them.

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

SERIALIZABLE

This level is like REPEATABLE READ, but InnoDB implicitly converts all plain SELECT statements to SELECT ... LOCK IN SHARE MODE if autocommit is disabled.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top