- Class ReentrantLock
- Guide to java.util.concurrent.Locks
- Get started with Spring 5 and Spring Boot 2, through the Learn Spring course:
- 1. Overview
- 2. Differences Between Lock and Synchronized Block
- 3. Lock API
- 4. Lock Implementations
- 4.1. ReentrantLock
- 4.2. ReentrantReadWriteLock
- 4.3. StampedLock
- 5. Working With Conditions
- 6. Conclusion
Class ReentrantLock
A reentrant mutual exclusion Lock with the same basic behavior and semantics as the implicit monitor lock accessed using synchronized methods and statements, but with extended capabilities.
A ReentrantLock is owned by the thread last successfully locking, but not yet unlocking it. A thread invoking lock will return, successfully acquiring the lock, when the lock is not owned by another thread. The method will return immediately if the current thread already owns the lock. This can be checked using methods isHeldByCurrentThread() , and getHoldCount() .
The constructor for this class accepts an optional fairness parameter. When set true , under contention, locks favor granting access to the longest-waiting thread. Otherwise this lock does not guarantee any particular access order. Programs using fair locks accessed by many threads may display lower overall throughput (i.e., are slower; often much slower) than those using the default setting, but have smaller variances in times to obtain locks and guarantee lack of starvation. Note however, that fairness of locks does not guarantee fairness of thread scheduling. Thus, one of many threads using a fair lock may obtain it multiple times in succession while other active threads are not progressing and not currently holding the lock. Also note that the untimed tryLock() method does not honor the fairness setting. It will succeed if the lock is available even if other threads are waiting.
It is recommended practice to always immediately follow a call to lock with a try block, most typically in a before/after construction such as:
In addition to implementing the Lock interface, this class defines a number of public and protected methods for inspecting the state of the lock. Some of these methods are only useful for instrumentation and monitoring.
Serialization of this class behaves in the same way as built-in locks: a deserialized lock is in the unlocked state, regardless of its state when serialized.
This lock supports a maximum of 2147483647 recursive locks by the same thread. Attempts to exceed this limit result in Error throws from locking methods.
Guide to java.util.concurrent.Locks
The Kubernetes ecosystem is huge and quite complex, so it’s easy to forget about costs when trying out all of the exciting tools.
To avoid overspending on your Kubernetes cluster, definitely have a look at the free K8s cost monitoring tool from the automation platform CAST AI. You can view your costs in real time, allocate them, calculate burn rates for projects, spot anomalies or spikes, and get insightful reports you can share with your team.
Connect your cluster and start monitoring your K8s costs right away:
We rely on other people’s code in our own work. Every day.
It might be the language you’re writing in, the framework you’re building on, or some esoteric piece of software that does one thing so well you never found the need to implement it yourself.
The problem is, of course, when things fall apart in production — debugging the implementation of a 3rd party library you have no intimate knowledge of is, to say the least, tricky.
Lightrun is a new kind of debugger.
It’s one geared specifically towards real-life production environments. Using Lightrun, you can drill down into running applications, including 3rd party dependencies, with real-time logs, snapshots, and metrics.
Learn more in this quick, 5-minute Lightrun tutorial:
Slow MySQL query performance is all too common. Of course it is. A good way to go is, naturally, a dedicated profiler that actually understands the ins and outs of MySQL.
The Jet Profiler was built for MySQL only, so it can do things like real-time query performance, focus on most used tables or most frequent queries, quickly identify performance issues and basically help you optimize your queries.
Critically, it has very minimal impact on your server’s performance, with most of the profiling work done separately — so it needs no server changes, agents or separate services.
Basically, you install the desktop application, connect to your MySQL server, hit the record button, and you’ll have results within minutes:
DbSchema is a super-flexible database designer, which can take you from designing the DB with your team all the way to safely deploying the schema.
The way it does all of that is by using a design model, a database-independent image of the schema, which can be shared in a team using GIT and compared or deployed on to any database.
And, of course, it can be heavily visual, allowing you to interact with the database using diagrams, visually compose queries, explore the data, generate random data, import data or build HTML5 database reports.
The Kubernetes ecosystem is huge and quite complex, so it’s easy to forget about costs when trying out all of the exciting tools.
To avoid overspending on your Kubernetes cluster, definitely have a look at the free K8s cost monitoring tool from the automation platform CAST AI. You can view your costs in real time, allocate them, calculate burn rates for projects, spot anomalies or spikes, and get insightful reports you can share with your team.
Connect your cluster and start monitoring your K8s costs right away:
Get started with Spring 5 and Spring Boot 2, through the Learn Spring course:
We’re looking for a new Java technical editor to help review new articles for the site.
1. Overview
Simply put, a lock is a more flexible and sophisticated thread synchronization mechanism than the standard synchronized block.
The Lock interface has been around since Java 1.5. It’s defined inside the java.util.concurrent.lock package, and it provides extensive operations for locking.
In this tutorial, we’ll explore different implementations of the Lock interface and their applications.
2. Differences Between Lock and Synchronized Block
There are a few differences between the use of synchronized block and using Lock APIs:
- A synchronizedblock is fully contained within a method. We can have Lock APIs lock() and unlock() operation in separate methods.
- A synchronized block doesn’t support the fairness. Any thread can acquire the lock once released, and no preference can be specified. We can achieve fairness within the Lock APIs by specifying the fairness property. It makes sure that the longest waiting thread is given access to the lock.
- A thread gets blocked if it can’t get an access to the synchronized block. The Lock API provides tryLock() method. The thread acquires lock only if it’s available and not held by any other thread. This reduces blocking time of thread waiting for the lock.
- A thread that is in “waiting” state to acquire the access to synchronized block can’t be interrupted. The Lock API provides a method lockInterruptibly() that can be used to interrupt the thread when it’s waiting for the lock.
3. Lock API
Let’s take a look at the methods in the Lock interface:
- void lock()– Acquire the lock if it’s available. If the lock isn’t available, a thread gets blocked until the lock is released.
- void lockInterruptibly() – This is similar to the lock(), but it allows the blocked thread to be interrupted and resume the execution through a thrown java.lang.InterruptedException.
- boolean tryLock()– This is a nonblocking version of lock() method. It attempts to acquire the lock immediately, return true if locking succeeds.
- boolean tryLock(long timeout, TimeUnit timeUnit)– This is similar to tryLock(), except it waits up the given timeout before giving up trying to acquire the Lock.
- void unlock() unlocks the Lock instance.
A locked instance should always be unlocked to avoid deadlock condition.
A recommended code block to use the lock should contain a try/catch and finally block:
Lock lock = . ; lock.lock(); try < // access to the shared resource >finally
In addition to the Lock interface, we have a ReadWriteLock interface that maintains a pair of locks, one for read-only operations and one for the write operation. The read lock may be simultaneously held by multiple threads as long as there is no write.
ReadWriteLock declares methods to acquire read or write locks:
- Lock readLock()returns the lock that’s used for reading.
- Lock writeLock() returns the lock that’s used for writing.
4. Lock Implementations
4.1. ReentrantLock
ReentrantLock class implements the Lock interface. It offers the same concurrency and memory semantics as the implicit monitor lock accessed using synchronized methods and statements, with extended capabilities.
Let’s see how we can use ReentrantLock for synchronization:
public class SharedObject < //. ReentrantLock lock = new ReentrantLock(); int counter = 0; public void perform() < lock.lock(); try < // Critical section here count++; >finally < lock.unlock(); >> //. >
We need to make sure that we are wrapping the lock() and the unlock() calls in the try-finally block to avoid the deadlock situations.
Let’s see how the tryLock() works:
public void performTryLock() < //. boolean isLockAcquired = lock.tryLock(1, TimeUnit.SECONDS); if(isLockAcquired) < try < //Critical section here >finally < lock.unlock(); >> //. >
In this case, the thread calling tryLock() will wait for one second and will give up waiting if the lock isn’t available.
4.2. ReentrantReadWriteLock
ReentrantReadWriteLock class implements the ReadWriteLock interface.
Let’s see the rules for acquiring the ReadLock or WriteLock by a thread:
- Read Lock – If no thread acquired the write lock or requested for it, multiple threads can acquire the read lock.
- Write Lock – If no threads are reading or writing, only one thread can acquire the write lock.
Let’s look at how to make use of the ReadWriteLock:
public class SynchronizedHashMapWithReadWriteLock < MapsyncHashMap = new HashMap<>(); ReadWriteLock lock = new ReentrantReadWriteLock(); // . Lock writeLock = lock.writeLock(); public void put(String key, String value) < try < writeLock.lock(); syncHashMap.put(key, value); >finally < writeLock.unlock(); >> . public String remove(String key) < try < writeLock.lock(); return syncHashMap.remove(key); >finally < writeLock.unlock(); >> //. >
For both write methods, we need to surround the critical section with the write lock — only one thread can get access to it:
Lock readLock = lock.readLock(); //. public String get(String key) < try < readLock.lock(); return syncHashMap.get(key); >finally < readLock.unlock(); >> public boolean containsKey(String key) < try < readLock.lock(); return syncHashMap.containsKey(key); >finally < readLock.unlock(); >>
For both read methods, we need to surround the critical section with the read lock. Multiple threads can get access to this section if no write operation is in progress.
4.3. StampedLock
StampedLock is introduced in Java 8. It also supports both read and write locks.
However, lock acquisition methods return a stamp that is used to release a lock or to check if the lock is still valid:
public class StampedLockDemo < Mapmap = new HashMap<>(); private StampedLock lock = new StampedLock(); public void put(String key, String value) < long stamp = lock.writeLock(); try < map.put(key, value); >finally < lock.unlockWrite(stamp); >> public String get(String key) throws InterruptedException < long stamp = lock.readLock(); try < return map.get(key); >finally < lock.unlockRead(stamp); >> >
Another feature provided by StampedLock is optimistic locking. Most of the time, read operations don’t need to wait for write operation completion, and as a result of this, the full-fledged read lock isn’t required.
Instead, we can upgrade to read lock:
public String readWithOptimisticLock(String key) < long stamp = lock.tryOptimisticRead(); String value = map.get(key); if(!lock.validate(stamp)) < stamp = lock.readLock(); try < return map.get(key); >finally < lock.unlock(stamp); >> return value; >
5. Working With Conditions
The Condition class provides the ability for a thread to wait for some condition to occur while executing the critical section.
This can occur when a thread acquires the access to the critical section but doesn’t have the necessary condition to perform its operation. For example, a reader thread can get access to the lock of a shared queue that still doesn’t have any data to consume.
Traditionally Java provides wait(), notify() and notifyAll() methods for thread intercommunication.
Conditions have similar mechanisms, but we can also specify multiple conditions:
public class ReentrantLockWithCondition < Stackstack = new Stack<>(); int CAPACITY = 5; ReentrantLock lock = new ReentrantLock(); Condition stackEmptyCondition = lock.newCondition(); Condition stackFullCondition = lock.newCondition(); public void pushToStack(String item) < try < lock.lock(); while(stack.size() == CAPACITY) < stackFullCondition.await(); >stack.push(item); stackEmptyCondition.signalAll(); > finally < lock.unlock(); >> public String popFromStack() < try < lock.lock(); while(stack.size() == 0) < stackEmptyCondition.await(); >return stack.pop(); > finally < stackFullCondition.signalAll(); lock.unlock(); >> >
6. Conclusion
In this article, we saw different implementations of the Lock interface and the newly introduced StampedLock class.
We also explored how we can make use of the Condition class to work with multiple conditions.
The complete code for this article is available over on GitHub.
Slow MySQL query performance is all too common. Of course it is. A good way to go is, naturally, a dedicated profiler that actually understands the ins and outs of MySQL.
The Jet Profiler was built for MySQL only, so it can do things like real-time query performance, focus on most used tables or most frequent queries, quickly identify performance issues and basically help you optimize your queries.
Critically, it has very minimal impact on your server’s performance, with most of the profiling work done separately — so it needs no server changes, agents or separate services.
Basically, you install the desktop application, connect to your MySQL server, hit the record button, and you’ll have results within minutes: