Summary: The synchronized keyword is provided in Java to ensure that only one thread can access the synchronized code block. Since the synchronized keyword has been provided, why is the Lock interface also provided in the Java SDK package? Is this unnecessary reinvention of the wheel? Today, we will discuss this issue together.
The synchronized
keyword is provided in Java to ensure that only one thread can access the synchronized code block. Since the synchronized
keyword has been provided, why is the Lock interface also provided in the Java SDK package? Is this unnecessary reinvention of the wheel? Today, we will discuss this issue together.
question?
Since the synchronized keyword is provided in the JVM to ensure that only one thread can access the synchronized code block, why do we need to provide the Lock interface? Is this reinventing the wheel? Why do Java designers do this? Let us look down together with questions.
Many friends may have heard that in Java 1.5 version, the performance of synchronized was not as good as Lock, but after Java 1.6 version, synchronized
made a lot of optimizations and the performance improved a lot. few. So since the performance of the synchronized keyword has been improved, why still use Lock?
If we think deeper, it is not difficult to think of it: we cannot actively release the lock when we use synchronized
to lock, which will involve the problem of deadlock.
If a deadlock is to occur, the following four necessary conditions must exist, and none of the four is indispensable.
##Mutually exclusive conditions
In a certain period of time The resource is only occupied by one thread. At this time, if other threads request the resource, the requesting thread can only wait.
Non-deprivable conditions
Request and hold conditions
Loop waiting conditions
synchronized keyword and a deadlock occurs, the key to synchronized is that it cannot destroy the "inalienable" deadlock. lock conditions. This is because when synchronized applies for resources, if it cannot apply, the thread directly enters the blocked state. When the thread enters the blocked state, it cannot do anything, and it cannot release the resources already occupied by the thread.
synchronized problem, how should we design it?
synchronized is that after holding lock A, if the attempt to acquire lock B fails, the thread will enter a blocked state. Once a deadlock occurs, there will be no chance to wake up the blocked thread. But if the blocked thread can respond to the interrupt signal, that is, when we send an interrupt signal to the blocked thread, we can wake it up, then it will have the opportunity to release the lock A it once held. This violates the inalienable condition.
(2) Support timeout. If the thread does not acquire the lock within a period of time and instead of entering the blocking state, returns an error, then the thread also has the opportunity to release the lock it once held. This would also undermine inalienable conditions.
(3) Acquire the lock non-blockingly. If the attempt to acquire the lock fails and it does not enter the blocking state, but returns directly, the thread will also have the opportunity to release the lock it once held. This would also undermine inalienable conditions.
is reflected in the Lock interface, which is the three methods provided by the Lock interface,
is as follows:
// 支持中断的API void lockInterruptibly() throws InterruptedException; // 支持超时的API boolean tryLock(long time, TimeUnit unit) throws InterruptedException; // 支持非阻塞获取锁的API boolean tryLock();
lockInterruptibly()
supports interrupts.
tryLock() method
The tryLock() method has a return value, which means it is used to try to acquire the lock. If the acquisition is successful, It returns true. If the acquisition fails (that is, the lock has been acquired by another thread), it returns false, which means that this method will return immediately no matter what. You won't be waiting there when you can't get the lock.
tryLock(long time, TimeUnit unit) method
tryLock
(long time, TimeUnit unit) method and tryLock The () method is similar, but the difference is that this method will wait for a certain period of time when it cannot get the lock. If it cannot get the lock within the time limit, it will return false. Returns true if the lock was obtained initially or during the waiting period.
In other words, for the deadlock problem, Lock can destroy the inalienable condition. For example, our program code below destroys the inalienable condition of the deadlock.
public class TansferAccount{ private Lock thisLock = new ReentrantLock(); private Lock targetLock = new ReentrantLock(); //账户的余额 private Integer balance; //转账操作 public void transfer(TansferAccount target, Integer transferMoney){ boolean isThisLock = thisLock.tryLock(); if(isThisLock){ try{ boolean isTargetLock = targetLock.tryLock(); if(isTargetLock){ try{ if(this.balance >= transferMoney){ this.balance -= transferMoney; target.balance += transferMoney; } }finally{ targetLock.unlock } } }finally{ thisLock.unlock(); } } } }
Exception, there is a ReentrantLock
under Lock, and ReentrantLock
supports fair locks and unfair locks.
When using ReentrantLock, there are two constructors in ReentrantLock, one is a parameterless constructor, and the other is a constructor that passes in the fair parameter. The fair parameter represents the fairness strategy of the lock. If true is passed in, it means that a fair lock needs to be constructed, otherwise it means that an unfair lock needs to be constructed. This is shown in the following code snippet.
//无参构造函数: 默认非公平锁 public ReentrantLock() { sync = new NonfairSync(); } //根据公平策略参数创建锁 public ReentrantLock(boolean fair){ sync = fair ? new FairSync() : new NonfairSync(); }
The implementation of locks essentially corresponds to an entry waiting queue. If a thread does not obtain the lock, it will enter the waiting queue. When a thread releases the lock, it needs to wake up from the waiting queue. Waiting thread. If it is a fair lock, the wake-up strategy is to wake up whoever has waited a long time, which is very fair; if it is an unfair lock, this fairness guarantee is not provided, and the thread with a short waiting time may be awakened first. Lock supports fair locks, but synchronized does not support fair locks.
Finally, it is worth noting that when using Lock to lock, the lock must be released in the finally{}
code block, for example, as shown in the following code snippet.
try{ lock.lock(); }finally{ lock.unlock(); }
Note: For other detailed descriptions of synchronized and Lock, friends can check it out by themselves.
The above is the detailed content of Why does Java need to provide Lock instead of just using the synchronized keyword?. For more information, please follow other related articles on the PHP Chinese website!