Home >Java >javaTutorial >Completely master Java locks (graphic and text analysis)
This article brings you relevant knowledge about java, which mainly introduces issues related to java locks, including exclusive locks, pessimistic locks, optimistic locks, shared locks, etc. , let’s take a look at it, I hope it will be helpful to everyone.
## Recommended study: "java video tutorial"
Optimistic lock and pessimistic lockPessimism Lock
Pessimistic Lock corresponds to pessimistic people in life. Pessimistic people always think about things going in the wrong direction.
synchronized and
ReentrantLock are typical pessimistic locks, and some use the synchronized keyword. Container classes such as
HashTable are also applications of pessimistic locking.
Optimistic Lock
Optimistic Lock Corresponds to optimistic people in life. Optimistic people always think about things developing in a good direction.
version number mechanism and
CAS algorithm. In the Java language, the atomic class under the
java.util.concurrent.atomic package is implemented using CAS optimistic locking.
Usage scenarios of the two locks
There is no better or worse between pessimistic lock and optimistic lock, and they have their own suitable scenarios. Optimistic locking is suitable for scenarios where there are relatively few writes (relatively small conflicts). Because there is no need to lock or release locks, the overhead of locks is eliminated, thus improving throughput. If it is a scenario with more writing and less reading, that is, the conflict is serious and competition between threads is stimulated, using optimistic lock will cause the thread to continuously retry, which may also reduce the performance. In this scenario, use pessimistic lock It's more appropriate. Exclusive lock and shared lockExclusive lock
Exclusive lock means that the lock can only be held by one thread at a time have. If a thread adds an exclusive lock to the data, other threads can no longer add any type of lock to the data. A thread that obtains an exclusive lock can both read and modify data.
synchronized and
java.util.concurrent(JUC) packages in the JDK is an exclusive lock.
Shared lock
Shared lock means that the lock can be held by multiple threads. If a thread adds a shared lock to the data, other threads can only add shared locks to the data and cannot add exclusive locks. The thread that obtains the shared lock can only read data and cannot modify the data.
ReentrantReadWriteLock is a shared lock.
Mutex lock
##Mutex lock is a conventional type of exclusive lock Implementation means that a certain resource allows only one visitor to access it at the same time, and is unique and exclusive.
#Mutex lock Only one thread can own the mutex lock at a time, and other threads can only wait.
Read-write lockRead-write lock
is a specific implementation of shared lock. Read-write locks manage a set of locks, one is a read-only lock and the other is a write lock. The read lock can be held by multiple threads at the same time when there is no write lock, and the write lock is exclusive. The priority of a write lock is higher than that of a read lock. A thread that obtains a read lock must be able to see the content updated by the previously released write lock.
Read-write locks have a higher degree of concurrency than mutex locks. There is only one writing thread at a time, but multiple threads can read concurrently at the same time.
A read-write lock interface is defined in the JDK: ReadWriteLock
public interface ReadWriteLock { /** * 获取读锁 */ Lock readLock(); /** * 获取写锁 */ Lock writeLock(); }
ReentrantReadWriteLock
implemented ReadWriteLock
interface, the specific implementation will not be expanded here, and the source code will be analyzed in depth later.
Fair lock
Fair lock
refers to the order in which multiple threads apply for locks Come and get the lock. It's like queuing up to buy a ticket. Those who come first buy first, and those who come later queue at the end of the queue. This is fair.
In java, fair locks can be initialized through the constructor
/** * 创建一个可重入锁,true 表示公平锁,false 表示非公平锁。默认非公平锁 */ Lock lock = new ReentrantLock(true);
Unfair lock
Unfair lock
means that the order in which multiple threads acquire locks is not in the order in which they apply for locks. It is possible that threads that apply later acquire locks before threads that apply first. In a high-concurrency environment, this may cause a priority flip. , or a starving state (a thread never gets the lock).
In java, the synchronized keyword is an unfair lock, and ReentrantLock is also an unfair lock by default.
/** * 创建一个可重入锁,true 表示公平锁,false 表示非公平锁。默认非公平锁 */ Lock lock = new ReentrantLock(false);
Reentrant lock
is also called recursive lock
, which means that the same thread acquires the lock in the outer method Lock, the lock will be automatically acquired when entering the inner method.
For Java ReentrantLock, its name can tell that it is a reentrant lock. For Synchronized, it is also a reentrant lock.
Knock on the blackboard: One benefit of reentrant locks is that deadlocks can be avoided to a certain extent.
Take synchronized as an example, look at the following code:
public synchronized void mehtodA() throws Exception{ // Do some magic tings mehtodB(); } public synchronized void mehtodB() throws Exception{ // Do some magic tings }
In the above code, methodA calls methodB. If a thread calls methodA and has acquired the lock and then calls methodB, there is no need to acquire it again. Locked, this is the characteristic of a reentrant lock. If it is not a reentrant lock, mehtodB may not be executed by the current thread, which may cause a deadlock.
Spin lock
means that the thread is not directly suspended when the lock is not obtained, but executes a busy loop. This busy loop is the so-called of spin.
#The purpose of the spin lock is to reduce the chance of the thread being suspended, because thread suspension and wake-up are also resource-consuming operations.
If the lock is occupied by another thread for a long time, the current thread will still be suspended even after spinning, and the busy loop will become a waste of system resources, which will actually reduce the overall performance. Therefore, spin locks are not suitable for concurrency situations where the lock takes a long time.
In Java, the AtomicInteger
class has a spin operation. Let’s take a look at the code:
public final int getAndAddInt(Object o, long offset, int delta) { int v; do { v = getIntVolatile(o, offset); } while (!compareAndSwapInt(o, offset, v, v + delta)); return v; }
If the CAS operation fails, it will keep looping to obtain the current value and then try again. try.
In addition, adaptive spin locks also need to be understood.
Adaptive spin was introduced in JDK1.6. This is more intelligent. The spin time is no longer fixed. It is determined by the previous spin time on the same lock and the status of the lock owner. to decide. If the virtual machine thinks that this spin is likely to succeed again, it will take more time. If the spin rarely succeeds, it may directly omit the spin process in the future to avoid wasting processor resources.
Segmented lock
is a lock design, not a specific lock.
The design purpose of segmented lock is to further refine the granularity of the lock. When the operation does not need to update the entire array, only one item in the array can be locked.
In the Java language, the underlying layer of CurrentHashMap uses segmentation locks. Using Segment, it can be used concurrently.
In order to improve performance and reduce the consumption caused by acquiring and releasing locks, JDK1.6 introduced 4 lock states: No lock
, biased lock
, Lightweight lock
and Heavyweight lock
, which will change with the number of locks. The thread's contention situation gradually escalates, but cannot degrade.
Lock-free
Lock-free
The state is actually the optimistic lock mentioned above, so I won’t go into details here.
Bias lock
Java偏向锁(Biased Locking)是指它会偏向于第一个访问锁的线程,如果在运行过程中,只有一个线程访问加锁的资源,不存在多线程竞争的情况,那么线程是不需要重复获取锁的,这种情况下,就会给线程加一个偏向锁。
偏向锁的实现是通过控制对象Mark Word
的标志位来实现的,如果当前是可偏向状态
,需要进一步判断对象头存储的线程 ID 是否与当前线程 ID 一致,如果一致直接进入。
轻量级锁
当线程竞争变得比较激烈时,偏向锁就会升级为轻量级锁
,轻量级锁认为虽然竞争是存在的,但是理想情况下竞争的程度很低,通过自旋方式
等待上一个线程释放锁。
重量级锁
如果线程并发进一步加剧,线程的自旋超过了一定次数,或者一个线程持有锁,一个线程在自旋,又来了第三个线程访问时(反正就是竞争继续加大了),轻量级锁就会膨胀为重量级锁
,重量级锁会使除了此时拥有锁的线程以外的线程都阻塞。
升级到重量级锁其实就是互斥锁了,一个线程拿到锁,其余线程都会处于阻塞等待状态。
在 Java 中,synchronized 关键字内部实现原理就是锁升级的过程:无锁 --> 偏向锁 --> 轻量级锁 --> 重量级锁。这一过程在后续讲解 synchronized 关键字的原理时会详细介绍。
锁粗化
锁粗化
就是将多个同步块的数量减少,并将单个同步块的作用范围扩大,本质上就是将多次上锁、解锁的请求合并为一次同步请求。
举个例子,一个循环体中有一个代码同步块,每次循环都会执行加锁解锁操作。
private static final Object LOCK = new Object(); for(int i = 0;i <p>经过<code>锁粗化</code>后就变成下面这个样子了:</p><pre class="brush:php;toolbar:false"> synchronized(LOCK){ for(int i = 0;i <p><strong>锁消除</strong></p><p><code>锁消除</code>是指虚拟机编译器在运行时检测到了共享数据没有竞争的锁,从而将这些锁进行消除。</p><p>举个例子让大家更好理解。</p><pre class="brush:php;toolbar:false">public String test(String s1, String s2){ StringBuffer stringBuffer = new StringBuffer(); stringBuffer.append(s1); stringBuffer.append(s2); return stringBuffer.toString(); }
上面代码中有一个 test 方法,主要作用是将字符串 s1 和字符串 s2 串联起来。
test 方法中三个变量s1, s2, stringBuffer, 它们都是局部变量,局部变量是在栈上的,栈是线程私有的,所以就算有多个线程访问 test 方法也是线程安全的。
我们都知道 StringBuffer 是线程安全的类,append 方法是同步方法,但是 test 方法本来就是线程安全的,为了提升效率,虚拟机帮我们消除了这些同步锁,这个过程就被称为锁消除
。
StringBuffer.class // append 是同步方法 public synchronized StringBuffer append(String str) { toStringCache = null; super.append(str); return this; }
前面讲了 Java 语言中各种各种的锁,最后再通过六个问题统一总结一下:
推荐学习:《java视频教程》
The above is the detailed content of Completely master Java locks (graphic and text analysis). For more information, please follow other related articles on the PHP Chinese website!