Pessimistic lock (Pessimistic lock)
As the name implies, is very pessimistic , every time to take the data when they think others will change, so every time when the data are locked, so that others want to take this data will block until it gets the lock. Traditional relational database in the use of a lot of this locking mechanism, such as row locks, table locks, read locks, write locks, etc., are in operation before the lock.
We think that the concurrency updates in the system are very frequent, and that the transaction fails and the cost of the re-start is huge, so that we need to implement the lock in a real sense. The basic idea of pessimistic locking is that every time a transaction reads a record, it locks the record so that other transactions want to be updated and must wait for the previous transaction to commit or rollback the unlock.
Implementation method:
Most of the locking operations are implemented at the database level, JDBC: Using pessimistic locks in JDBC requires the use of select for update statements, e.g.
Select * from Account where ...(where condition).. for update
Optimistic lock (Optimistic lock)
As the name implies, is very optimistic , every time to take the data when they think others will not be modified, so will not be locked, but in the update will be judged in this period when others have to update the data, you can use the version number mechanism. Optimistic locking is useful for multi-read application types, which can improve throughput, such as the fact that a database provides an optimistic lock similar to the write_condition mechanism.
We think that the transaction concurrency update in the system is not very frequent, even if the conflict is OK, the big deal again. The basic idea is that every time a transactional update is committed, we want to see if something has been modified since the last time it was read, and if it has been modified, the update will fail.
Implementation method:
Mostly based on the data version of the record mechanism implementation, what is the data version? is to add a version identity to the data, which is typically done by adding a "version" field to the database table in the version solution based on the database table.
When the data is read, the version number is read together, and then the version number is added one after the update. At this point, the version data of the submitted data is compared to the current version information of the database table corresponding to the record, and if the submitted version number is greater than the current version number of the database table, it is updated, otherwise it is considered to be outdated data.
If there is an entity class for account in the system, we add a version field to the account, then our JDBC SQL statement will read as follows:
e.g.
Select a.version....from Account as a where (where condition..)Update Account set version = version+1.....(another field) where version =?...(another contidition)
In this way we can judge by updating the number of rows of results, if the number of rows for the update result is 0, then the entity has been changed by other transactions since it was loaded, so the optimistic locking exception is thrown out of the definition. The concrete examples are as follows:
int rowsUpdated = statement.executeUpdate(sql);if (rowsUpdated ==0 ) { throwsnew OptimisticLockingFailureException();}
Implementation of pessimistic lock
synchronized Mutex is a pessimistic lock, it has an obvious disadvantage, it regardless of the existence of data storage competition is locked, with the increase in concurrency, and if the lock time is longer, its performance costs will become very large. Is there a way to solve this problem? The answer is optimistic locking based on conflict detection. In this mode, there is no so-called lock concept, each thread directly first to perform operations, after the completion of the calculation to detect the existence of shared data with other threads to compete, if not to make this operation successful, if there is a competition for shared data can continue to re-perform operations and detection, until successful, this is called CAS spin.
Compareandset (CAS) in Java
Take the implementation of Atomicinteger Incrementandget as an example:
The realization of Incrementandget
publicfinalintincrementAndGet() { for (;;) { int current = get(); int1; if (compareAndSet(current, next)) return next; } }
First you can see that he is through an infinite loop (spin) until increment succeeds.
The contents of the loop are:
- Get Current value
- Calculate the value after +1
- If the current value is still valid (not being), set the value after that +1.
- If the setting is unsuccessful (the current value is invalid, it is changed by another thread), starting with 1.
The realization of Compareandset
publicfinalbooleancompareAndSet(intint update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update);}
The direct call is the Compareandswapint method of the Unsafe class, the full name is Sun.misc.Unsafe. This class is an implementation provided by Oracle (SUN), which may not be the class in the other company's JDK.
The realization of Compareandswapint
/** * Atomically update Java variable to <tt>x</tt> if it is currently * holding <tt>expected</tt>. * @return <tt>true</tt> if successful */ publiclongintint x);
This method is not implemented in Java, but is called by JNI to invoke the operating system's native program, involving CPU atomic operations, now almost all CPU instructions are supported CAS atomic operation, X86 corresponding to the CMPXCHG assembly instructions.
Out of curiosity, I looked at the code description for the next CAs atom operation:
int compare_and_swap(intintint newval) { ATOMIC(); int old_reg_val = *reg; if (old_reg_val == oldval) *reg = newval; END_ATOMIC(); return old_reg_val;}
That is to check if the value in memory *reg is oldval, and if so, it is assigned a value newval. The above code always returns Old_reg_value, and if the caller needs to know whether the update succeeds still needs to make further judgment, for convenience, it can be variant for the direct return whether the update succeeds, as follows:
bool compare_and_swap (intintint newval){ if ( *accum == *dest ) { *dest = newval; returntrue; } returnfalse;}
Comparison of two types of locks
The two kinds of locks have advantages and disadvantages, not to think of one better than the other, like the optimistic lock for less write, that is, the conflict really rarely occurs, this can save the lock overhead, increase the overall system throughput. However, if there is frequent conflict, the upper application will continue to retry, which is to reduce the performance, so in this case, pessimistic locking is more appropriate.
Comparison and use of pessimistic lock and optimistic lock