--Declaring a code block as synchronized has two important consequences, usually refers to the code having atomicity and visibility.
1.1 Atomicity
--Atomicity means that at a moment, only one thread can execute a piece of code, which is protected by a monitor object. This prevents multiple threads from *ing with each other when updating the shared state.
1.2 Visibility
--Visibility is more subtle, it deals with various abnormal behaviors of memory cache and compiler optimization. It must ensure that changes made to the shared data before the lock is released for another that subsequently acquires the lock.
The thread is visible.
1.3 Function: Without this visibility guarantee provided by the synchronization mechanism, the shared variables seen by the thread may be pre-modified values or inconsistent values, which will cause many serious problems.
Limitations of
synchronized is good, but it is not perfect. It has some functional limitations:--It cannot interrupt a thread waiting to obtain the lock;
--You can't get the lock through voting, and if you don't want to wait, you can't get the lock;
--Synchronization also requires that the release of the lock can only be done in the same stack frame as the stack frame where the lock is obtained, which is fine in most cases (and interacts well with exception handling), but, it does exist
Some non-blocking locking is more suitable.
Implementation class
--Lock interface has three implementation classes, one is ReentrantLock, and the other two are two static inner classes ReadLock and WriteLock in the ReentrantReadWriteLock class.--Read-write locking allows for higher levels of concurrent access to shared data than mutex locking. Although only one thread at a time (writer thread) can modify shared data, in many cases,
Any number of threads can read shared data at the same time (reader thread). In theory, the concurrency enhancement allowed by using read-write locks will lead to greater performance improvements compared to mutex locks.
--In practice, concurrency enhancement can be fully achieved only on multiprocessors and only when access mode is applicable to shared data. ——For example, a certain person initially populated with data and does not often follow it
Modified collections, because they are often searched (such as searching for a certain directory), such collections are ideal candidates for using read-write locks.
Principle
The principle of reentrant lock is to maintain a thread mark inside the lock, indicating that the lock is currently occupied by the thread, and then associate a counter. The counter value is 0 at the beginning, indicating that the lock is not occupied by any thread.
When a thread acquires the lock, the counter will become 1. When other threads acquire the lock, they find that the owner of the lock is not themselves, so they are blocked.
However, when the thread that acquires the lock acquires the lock again, it finds that the lock owner will put the counter value +1, and the counter will be -1 after the lock is released. When the counter is 0, the thread mark in the lock is reset to null.
When a thread acquires the lock, the counter will become 1. When other threads acquire the lock, they find that the owner of the lock is not themselves, so they are blocked.
However, when the thread that acquires the lock acquires the lock again, it finds that the lock owner will put the counter value +1, and the counter will be -1 after the lock is released. When the counter is 0, the thread mark in the lock is reset to null.
Waiting for blocking threads to get the lock is awakened.
The --ReentrantLock class implements Lock, which has the same concurrency and memory semantics as synchronized, but adds some features similar to lock voting, timed lock waiting and interruptible lock waiting.In addition, it provides better performance in case of intense competition. (In other words, when many threads want to access shared resources, the JVM can spend less time dispatching threads and spend more time
Used on execution thread. )
Extended features
5.1 Implement pollable lock requests
--In the internal lock, deadlock is fatal - the only way to recover is to restart the program, and the only way to prevent it is to not make any mistakes when building the program. The pollable lock acquisition mode has more complete errorsThe recovery mechanism can avoid the occurrence of deadlocks.
--If you can't get all the locks you need, then using a pollable acquisition method allows you to regain control, which releases the locks you've already acquired and then try again.
The pollable lock acquisition mode is implemented by the tryLock() method. This method acquires the lock only when it is called. If the lock is available, the lock is acquired and the value is immediately returned to true.
If the lock is not available, this method will immediately return the value false. Typical usage statements for this method are as follows:
Lock lock = ...;
if (()) {
try {
// manipulate protected state
} finally {
();
}
} else {
// perform alternative actions
}
5.2 Implementing timeable lock requests
--When using an internal lock, once the request starts, the lock cannot be stopped, so the internal lock poses a risk to implementing time-limited activities. To solve this problem, a timed lock can be used.When the blocking method is called by the activity with a time limit, the timing lock can set the corresponding timeout within the time budget. If the activity fails to obtain results within the expected time, the timing lock can cause the program to return in advance.
The timeable lock acquisition mode is implemented by the tryLock(long, TimeUnit) method.
5.3 Implement interruptible lock acquisition request
--Interruptible lock acquisition operation allows use in cancelable activities. The lockInterruptibly() method can cause you to respond to interrupts when you obtain a lock.Bad things to pay attention to
--lock must be released in the finally block. Otherwise, if the protected code will throw an exception, the lock may never be released! This difference may not seem like nothing, but in fact,It is extremely important. Forgot to release the lock in the finally block, it may leave a time bomb in the program, and when the bomb explodes one day, it will take a lot of effort to find where the source is.
Using synchronization, the JVM will ensure that the lock will be automatically released.
--When the JVM uses synchronized to manage lock requests and releases, the JVM can include lock information when generating thread dumps. These are very valuable for debugging because they can identify deadlocks or
Source of other abnormal behaviors. The Lock class is just an ordinary class, and the JVM does not know which thread owns the Lock object.
7. Inter-thread communication Condition
--Condition can replace traditional inter-thread communication, replace wait() with await(), replace notify() with signal(), and replace notifyAll() with signalAll().--Why is the method name not called wait()/notify()/nofityAll() directly? Because these methods of Object are final and cannot be rewritten!
--The communication method of traditional threads can be implemented.
Condition is bound to a Lock. To create a Lock, the newCondition() method must be used.
The power of Condition is that it can create different Conditions between multiple threads.