There are many kinds of lock classification in Java multithreading, one of the main classification methods is optimistic and pessimistic division. This article mainly introduces how to write an optimistic lock code by hand. However, in order to ensure the integrity of the article, it will be introduced from the basics.
First, the concept of optimistic locking
It is said to write the concept of optimistic lock, but usually the concept of optimistic lock and pessimistic lock should be written together. It makes more sense in comparison.
1. Pessimistic lock concept
Pessimistic locking: Always assume the worst, every time you go to get data you think someone else will change it, so every time you get data you lock it, so that someone else tries to get the data and it blocks until it gets the lock.
This pessimistic locking mechanism is also used in databases. Such as row locks, table locks, read locks, write locks, etc., are locked before the operation. Other threads cannot synchronize operations until it is released.
2. Optimistic locking concept
Optimistic locking: Always assume the best case scenario, every time you go to get data, you assume that no one else will change it, so you don’t lock it, and only judge whether someone else has updated the data in the meantime.
3, optimistic lock implementation cases
There are three main features to consider in Java concurrency: atomicity, visibility, and orderliness. The purpose of AtomicInteger is to ensure atomicity. How do you guarantee atomicity? Our use case notes:
For a++ operations, there are actually three steps.
(1) Read the value of A from main memory
(2) Add 1 to a
(3) Refresh A to main memory
For example, one thread increments a by 1, but before it can be rewritten to main memory, another thread reads the old value. That was the mistake. The solution is to use AtomicInteger:
4. Optimistic lock case analysis
AtomicInteger is an optimistic lock, which means that if we look at how AtomicInteger implements this mechanism and principle, we can find out the general mechanism for other optimistic lock implementations. To find out, we need to start with AtomicInteger’s incrementAndGet method. Because this method implements a lock-like function. The jdk1.8 version is used here, there are some differences between different versions.
(1) Unsafe: Unsafe is a class in the Sun. misc package that gives the Java language the ability to manipulate memory Spaces like Pointers in THE C language. So we’re directly manipulating memory and adding one.
(2) unsafe. GetAndAddInt: The Unsafe.compareAndSwapInt method is also called inside. This mechanism is called the CAS mechanism,
CAS is a technique often used to implement concurrent algorithms by comparing and replacing. The CAS operation contains three operands — memory location, expected original value, and new value. When the CAS operation is performed, the value of the memory location is compared to the expected value. If it matches, the processor automatically updates the value of the memory location to the new value; otherwise, the processor does nothing.
Let’s use an example to explain it and I’m sure you’ll get a little clearer.
Like getting engaged to your son. Your son is the memory location. You thought your son was with Yang Guifei, but when you got engaged, you found xi Shi beside your son. What to do at this point? You don’t do anything in anger. If your son’s side is expected to be Yang Guifei, you look very happy to give them engaged, also known as the implementation of the operation. Now you get the idea.
But there is a common problem with this CAS mechanism. That’s the ABA problem, for example, if you see $100 on the table, and you go to do something else, and you come back and you see $100 still on the table, and you think that $100 hasn’t been touched, but in fact, while you were gone, someone else took $100 and returned it. That’s the ABA problem.
5. Optimistic thinking
Optimistic locking can be implemented by CAS + versioning.
Optimistic locking assumes that data will not have concurrent conflicts in general, so when data is submitted for update, it will formally detect whether the data has concurrent conflicts. If the data is found to have concurrent conflicts, the error message will be returned to the user, so that the user can decide what to do.
(1) CAS mechanism: When multiple threads try to use CAS to update the same variable at the same time, only one thread can update the value of the variable, while the other threads fail. CAS effectively says “I think position V should contain the value A; If this value is included, place B in this position; Otherwise, do not change the location, just tell me the current value of the location “.
(2) Version mechanism: CAS mechanism ensures that data is not changed to other data synchronization mechanism when updated, and version mechanism ensures that the synchronization mechanism is not changed (meaning the ABA problem above).
Based on this idea we can implement an optimistic lock. So let’s write some code. This code has been tested on my own computer.
Implement an optimistic lock
Step 1: Define the data we want to manipulate
Step 4: Output the results
This result can be seen as long as there is no change when reading the data, but when updating the data, it is necessary to judge whether the current version number is consistent with the expected version number. If so, it is updated; if not, it indicates that the update failed.