Welcome to Java Lock example tutorial. Usually when working with multi-threaded environment, we use synchronized for thread safety.
欢迎使用Java Lock示例教程。 通常,在多线程环境中使用时, 为了确保线程安全 ,我们使用sync 。
Java锁 (Java Lock)
Most of the times, synchronized keyword is the way to go but it has some shortcomings that lead the way to inclusion of Lock API in Java Concurrency package. Java 1.5 Concurrency API came up with java.util.concurrent.locks
package with Lock
interface and some implementation classes to improve the Object locking mechanism.
在大多数情况下,synchronized关键字是解决之道,但它存在一些缺点,导致将Lock API包含在Java Concurrency包中。 Java 1.5 Concurrency API带有带有Lock
接口的java.util.concurrent.locks
包和一些实现类,用于改进对象锁定机制。
Some important interfaces and classes in Java Lock API are:
Java Lock API中一些重要的接口和类是:
- Lock: This is the base interface for Lock API. It provides all the features of synchronized keyword with additional ways to create different Conditions for locking, providing timeout for thread to wait for lock. Some of the important methods are lock() to acquire the lock, unlock() to release the lock, tryLock() to wait for lock for a certain period of time, newCondition() to create the Condition etc. Lock :这是Lock API的基本接口。 它提供了synced关键字的所有功能以及创建不同锁定条件的其他方式,从而为线程等待锁定提供了超时。 一些重要的方法是使用lock()获取锁,使用unlock()释放锁,使用tryLock()等待一段时间的锁,使用newCondition()创建Condition等。
- Condition: Condition objects are similar to Object wait-notify model with additional feature to create different sets of wait. A Condition object is always created by Lock object. Some of the important methods are await() that is similar to wait() and signal(), signalAll() that is similar to notify() and notifyAll() methods. Condition :Condition对象类似于Object wait-notify模型,具有附加功能,可以创建不同的等待集。 Condition对象始终由Lock对象创建。 一些重要的方法是类似于wait()和signal()的await()和类似于notify()和notifyAll()方法的signalAll()。
- ReadWriteLock: It contains a pair of associated locks, one for read-only operations and another one for writing. The read lock may be held simultaneously by multiple reader threads as long as there are no writer threads. The write lock is exclusive. ReadWriteLock :它包含一对关联的锁,一个用于只读操作,另一个用于写入。 只要没有写程序线程,读锁就可以同时由多个读程序线程持有。 写锁是排他的。
- ReentrantLock: This is the most widely used implementation class of Lock interface. This class implements the Lock interface in similar way as synchronized keyword. Apart from Lock interface implementation, ReentrantLock contains some utility methods to get the thread holding the lock, threads waiting to acquire the lock etc.
synchronized block are reentrant in nature i.e if a thread has lock on the monitor object and if another synchronized block requires to have the lock on the same monitor object then thread can enter that code block. I think this is the reason for the class name to be ReentrantLock. Let’s understand this feature with a simple example.
public class Test{ public synchronized foo(){ //do something bar(); } public synchronized bar(){ //do some more } }
If a thread enters foo(), it has the lock on Test object, so when it tries to execute bar() method, the thread is allowed to execute bar() method since it’s already holding the lock on the Test object i.e same as synchronized(this).
ReentrantLock :这是Lock接口使用最广泛的实现类。 此类以与synced关键字相似的方式实现Lock接口。 除了Lock接口的实现之外,ReentrantLock还包含一些实用程序方法来获取持有锁的线程,等待获取锁的线程等。同步块本质上是可重入的,即,如果线程在监视对象上具有锁,并且如果另一个同步块需要在同一监视对象上具有锁,则线程可以输入该代码块。 我认为这是类名称为ReentrantLock的原因。 让我们通过一个简单的示例来了解此功能。
如果线程输入foo(),则它具有Test对象的锁定,因此,当它尝试执行bar()方法时,由于该线程已经持有Test对象的锁定,因此允许该线程执行bar()方法。同步(this)。
Java锁示例– Java中的ReentrantLock (Java Lock Example – ReentrantLock in Java)
Now let’s see a simple example where we will replace synchronized keyword with Java Lock API.
现在,让我们看一个简单的示例,其中将用Java Lock API替换synced关键字。
Let’s say we have a Resource class with some operation where we want it to be thread-safe and some methods where thread safety is not required.
假设我们有一个Resource类,其中包含一些我们希望它是线程安全的操作,以及一些不需要线程安全的方法。
package com.journaldev.threads.lock;
public class Resource {
public void doSomething(){
//do some operation, DB read, write etc
}
public void doLogging(){
//logging, no need for thread safety
}
}
Now let’s say we have a Runnable class where we will use Resource methods.
现在,我们有一个Runnable类,在其中我们将使用Resource方法。
package com.journaldev.threads.lock;
public class SynchronizedLockExample implements Runnable{
private Resource resource;
public SynchronizedLockExample(Resource r){
this.resource = r;
}
@Override
public void run() {
synchronized (resource) {
resource.doSomething();
}
resource.doLogging();
}
}
Notice that I am using synchronized block to acquire the lock on Resource object. We could have created a dummy object in the class and used that for locking purpose.
请注意,我正在使用同步块来获取对Resource对象的锁定。 我们可以在类中创建一个虚拟对象,并将其用于锁定目的。
Now let’s see how we can use java Lock API and rewrite above program without using synchronized keyword. We will use ReentrantLock in java.
现在让我们看看如何使用Java Lock API并在不使用synced关键字的情况下重写上述程序。 我们将在Java中使用ReentrantLock。
package com.journaldev.threads.lock;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ConcurrencyLockExample implements Runnable{
private Resource resource;
private Lock lock;
public ConcurrencyLockExample(Resource r){
this.resource = r;
this.lock = new ReentrantLock();
}
@Override
public void run() {
try {
if(lock.tryLock(10, TimeUnit.SECONDS)){
resource.doSomething();
}
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
//release lock
lock.unlock();
}
resource.doLogging();
}
}
As you can see that, I am using tryLock() method to make sure my thread waits only for definite time and if it’s not getting the lock on object, it’s just logging and exiting. Another important point to note is the use of try-finally block to make sure lock is released even if doSomething() method call throws any exception.
如您所见,我正在使用tryLock()方法来确保我的线程仅等待确定的时间,并且如果它没有获得对对象的锁定,那么它只是在记录并退出。 要注意的另一个重要点是,即使doSomething()方法调用引发任何异常,也要使用try-finally块来确保释放锁定。
Java Lock与同步 (Java Lock vs synchronized)
Based on above details and program, we can easily conclude following differences between Java Lock and synchronization.
根据上述详细信息和程序,我们可以轻松得出Java Lock和同步之间的以下区别。
- Java Lock API provides more visibility and options for locking, unlike synchronized where a thread might end up waiting indefinitely for the lock, we can use tryLock() to make sure thread waits for specific time only. Java Lock API提供了更多的锁定可见性和选项,与同步机制不同,同步机制可能导致线程无限期地等待锁定,因此我们可以使用tryLock()来确保线程仅在特定时间等待。
- Synchronization code is much cleaner and easy to maintain whereas with Lock we are forced to have try-finally block to make sure Lock is released even if some exception is thrown between lock() and unlock() method calls. 同步代码更简洁,易于维护,而使用Lock时,即使在lock()和unlock()方法调用之间引发了某些异常,我们也不得不尝试进行最后锁定,以确保释放Lock。
- synchronization blocks or methods can cover only one method whereas we can acquire the lock in one method and release it in another method with Lock API. 同步块或方法只能覆盖一个方法,而我们可以使用Lock API在一个方法中获取锁并在另一方法中释放锁。
- synchronized keyword doesn’t provide fairness whereas we can set fairness to true while creating ReentrantLock object so that longest waiting thread gets the lock first. synced关键字不提供公平性,而我们可以在创建ReentrantLock对象时将公平性设置为true,以便等待时间最长的线程首先获得该锁。
- We can create different conditions for Lock and different thread can await() for different conditions. 我们可以为Lock创建不同的条件,并且不同的线程可以为不同的条件使用await()。
That’s all for Java Lock example, ReentrantLock in java and a comparative analysis with synchronized keyword.
Java锁示例,Java中的ReentrantLock以及带有synced关键字的比较分析就可以了。
翻译自: https://www.journaldev.com/2377/java-lock-example-reentrantlock