In this article we will see how to acquire a ReentrantLock
in an interruptible and non-interruptible mode. When a thread fails to acquire the lock, it gets added to a waiting queue. It can be unblocked either by the thread holding the lock or by interrupting it.
Non-Interruptible Mode
When we call ReentrantLock.lock()
, we acquire lock in a non-interruptible mode which means even if the thread waiting in queue is interrupted or unparked, it is going to simply retry acquiring the lock. This goes on, resulting in blocking and unblocking of the thread till it acquires the lock.
Interruptible Mode
We can try acquiring lock in exclusive interruptible mode using ReentrantLock.lockInterruptibly()
in which case if the thread in waiting is interrupted by some other thread it will result in InterruptedException
thus there won’t be any retry. If the thread is unparked as a result of release of lock then it is going to retry acquiring lock.
Interruptible Mode Example
AcquireLockRunnable
acquires lock in either interruptible or non-interruptible mode based on the parameter passes in to the constructor. Once the thread acquires the lock, it sleeps for few seconds and releases the lock in the finally block. The first thread that acquires the lock, holds the lock a bit longer than the other threads so that the threads trying to acquire gets added to the queue.
AcquireLockRunnable:
package com.javarticles.threads; import java.util.concurrent.locks.ReentrantLock; public class AcquireLockRunnable implements Runnable { int id; boolean interruptable; ReentrantLock lock; AcquireLockRunnable(ReentrantLock lock, int id) { this(lock, id, true); } AcquireLockRunnable(ReentrantLock lock, int id, boolean interruptable) { this.lock = lock; this.id = id; this.interruptable = interruptable; } public void run() { print("Try lock"); try { if (interruptable) { lock.lockInterruptibly(); } else { lock.lock(); } } catch (InterruptedException e) { print("Acquiring lock failed due to " + e); return; } print("Got lock(" + id + ")"); try { try { if (id == 1) { Thread.sleep(3000); } else { Thread.sleep(2500); } } catch (InterruptedException e) { print("Sleep interrupted"); } } finally { lock.unlock(); print("Unlocked(" + id + ")"); } } static void print(String p) { System.out.println(Thread.currentThread().getName() + ": " + p); } }
In our first example, we acquire the lock in an interruptible mode. In method lockAndInterrupt()
, we start multiple threads. Each thread tries to acquire the lock. After the first thread is created and started, we sleep for couple of seconds so that the first thread gets a chance to acquire the lock. We pass an instance of AcquireLockRunnable
as the Runnable
. We then start the other threads which end up waiting in queue till they acquire the lock or are interrupted.
ReentrantLockInterruptableExample:
package com.javarticles.threads; import java.util.concurrent.locks.ReentrantLock; import static com.javarticles.threads.AcquireLockRunnable.print; public class ReentrantLockInterruptableExample { private static ReentrantLock lock = new ReentrantLock(); private boolean interruptable; ReentrantLockInterruptableExample() { this(true); } ReentrantLockInterruptableExample(boolean interruptable) { this.interruptable = interruptable; } public static void main(String[] args) throws InterruptedException { ReentrantLockInterruptableExample lockInterruptable = new ReentrantLockInterruptableExample(); lockInterruptable.lockAndInterrupt(); } void lockAndInterrupt() throws InterruptedException { Thread firstThread = new Thread(new AcquireLockRunnable(lock, 1, interruptable), "Thread(1)"); firstThread.start(); Thread.sleep(2000); Thread[] others = new Thread[6]; for (int i = 2; i < 8; i++) { others[i - 2]= new Thread(new AcquireLockRunnable(lock, i, interruptable), "Thread(" + i + ")"); others[i - 2].start(); } print("Interrupt threads"); for (int i = 0; i < 6; i++) { Thread.sleep(500 * i / 2); print("Interrupt " + others[i].getName()); others[i].interrupt(); } } }
Once all the threads are started, we interrupt threads in the order in which we have started. You can see from the below output, ‘Thread(1)’ acquires the lock first and remain threads try to acquire but fail till the first the thread doesn’t release the lock. Moment ‘Thread(1)’ release the lock, ‘Thread(5)’ ends up acquiring the lock as ‘Thread(2)’, ‘Thread(3)’ and ‘Thread(4)’ were interrupted which results in java.lang.InterruptedException
so they no more contest for the lock. ‘Thread(5)’ is interrupted and then it releases the lock which is then acquired by ‘Thread(6)’.
Output:
Thread(1): Try lock Thread(1): Got lock(1) Thread(2): Try lock Thread(3): Try lock Thread(4): Try lock main: Interrupt threads main: Interrupt Thread(2) Thread(5): Try lock Thread(6): Try lock Thread(2): Acquiring lock failed due to java.lang.InterruptedException Thread(7): Try lock main: Interrupt Thread(3) Thread(3): Acquiring lock failed due to java.lang.InterruptedException main: Interrupt Thread(4) Thread(4): Acquiring lock failed due to java.lang.InterruptedException Thread(1): Unlocked(1) Thread(5): Got lock(5) main: Interrupt Thread(5) Thread(5): Sleep interrupted Thread(5): Unlocked(5) Thread(6): Got lock(6) main: Interrupt Thread(6) Thread(6): Sleep interrupted Thread(6): Unlocked(6) Thread(7): Got lock(7) main: Interrupt Thread(7) Thread(7): Sleep interrupted Thread(7): Unlocked(7)
Non-Interruptable Mode Example
I our next example, we acquire the lock in a non-interruptible mode which is the default mode.
ReentrantLockInterruptableExample:
package com.javarticles.threads; public class ReentrantLockIgnoreInterruptionExample { public static void main(String[] args) throws InterruptedException { ReentrantLockInterruptableExample lockInterruptable = new ReentrantLockInterruptableExample(false); lockInterruptable.lockAndInterrupt(); } }
As you can see below, even though the threads are interruptes, it doesn’t result in java.lang.InterruptedException
, instead they simply try again to acquire the lock.
Output:
Thread(1): Try lock Thread(1): Got lock(1) Thread(2): Try lock Thread(3): Try lock Thread(4): Try lock main: Interrupt threads Thread(6): Try lock Thread(5): Try lock Thread(7): Try lock main: Interrupt Thread(2) main: Interrupt Thread(3) main: Interrupt Thread(4) Thread(1): Unlocked(1) Thread(2): Got lock(2) Thread(2): Sleep interrupted Thread(2): Unlocked(2) Thread(3): Got lock(3) Thread(3): Sleep interrupted Thread(3): Unlocked(3) Thread(4): Got lock(4) Thread(4): Sleep interrupted Thread(4): Unlocked(4) Thread(6): Got lock(6) main: Interrupt Thread(5) main: Interrupt Thread(6) Thread(6): Sleep interrupted Thread(6): Unlocked(6) Thread(5): Got lock(5) Thread(5): Sleep interrupted Thread(5): Unlocked(5) Thread(7): Got lock(7) main: Interrupt Thread(7) Thread(7): Sleep interrupted Thread(7): Unlocked(7)
Download the source code
This was an example about interrupting a thread trying to acquire a ReentrantLock
.