The ReentrantLock class is an implementation of Lock Interface, which will lock() the execution block for the thread which calls it.
Until the locked thread calls the lock.unlock() method, the code block in between will not be available to other Threads…even when it is idle or sleeping or taking long time.
Hence the block after lock.lock() is thread safe until the calling thread calls lock.unlock().
In this example, we have created simply 4 threads parallely to act on a single Object.
We use Reentrant Lock Object to lock the code block, from other threads, making one thread executing the code block.
we could see in results, until one thread completes the block, other thread will be waiting for to enter the code block.
If we remove the lock object, we could see all threads will call the methods parallely and executing the code block at same instant change the values concurrently.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
package my.test.learning; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class ReentrantLockExample { static String lowerCaseWord = "wonderfultimes"; public static void main(String[] args) { //Single Object ChangeWordCase changeWordCase = new ChangeWordCase(); //Single Object run by 4 threads Thread concurrencyThread = new Thread(new ConcurrencyLockThread(changeWordCase)); Thread concurrencyThread1 = new Thread(new ConcurrencyLockThread(changeWordCase)); Thread concurrencyThread2 = new Thread(new ConcurrencyLockThread(changeWordCase)); Thread concurrencyThread3 = new Thread(new ConcurrencyLockThread(changeWordCase)); concurrencyThread.start(); concurrencyThread1.start(); concurrencyThread2.start(); concurrencyThread3.start(); } } /** * This Utility class will convert any letters to uppercase., one thread at a time. * The ReentrantLock class is an implementation of Lock Interface, which will lock() the code snippet for the thread which calls it. * Until the locked thread calls the lock.unlock() method, the code block in between will not be available to other Threads...even when it is idle or sleeping or taking long time. * Hence the block after lock.lock() is thread safe. */ class ChangeWordCase { Lock lock =null; public ChangeWordCase() { lock = new ReentrantLock(); } public void doConversion(String word) { try { lock.lock(); System.out.println("******* Lock Acquired by Thread :" + Thread.currentThread().getName()); System.out.println("Incoming Word:" + word); String cWord = toUpperCase(word); display(cWord); } catch(Exception ig){} finally { lock.unlock(); System.out.println("******* Lock Released by Thread :" + Thread.currentThread().getName()); } } private String toUpperCase(String word) { System.out.println("Method toUpperCase Entered :" + word); char[] c = word.toCharArray(); for(int i=0;i<c.length;i++) { int x = c[i]; if(x>=97 && x<=122) { x = x-32; } c[i] = (char)x; } try { Thread.sleep(1000); } catch(Exception e){} return String.valueOf(c); } private void display(String word) { System.out.println("Executed Display() Method - Temp Word:"+ word); } } class ConcurrencyLockThread implements Runnable { ChangeWordCase changeWordCase =null; public ConcurrencyLockThread(ChangeWordCase changeWordCase) { this.changeWordCase = changeWordCase; } @Override public void run() { try { changeWordCase.doConversion(Thread.currentThread().getName()); } catch (Exception e) { e.printStackTrace(); } } } |
Here we could see until the thread release the lock, other thread cannot acquire/take the lock.
Remaining Threads are waiting for the current thread to release the lock.
Result:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
*******Lock Acquired by Thread :Thread-0 Incoming Word:Thread-0 Method toUpperCase Entered :Thread-0 Executed Display() Method - Temp Word:THREAD-0 *******Lock Released by Thread :Thread-0 *******Lock Acquired by Thread :Thread-1 Incoming Word:Thread-1 Method toUpperCase Entered :Thread-1 Executed Display() Method - Temp Word:THREAD-1 *******Lock Released by Thread :Thread-1 *******Lock Acquired by Thread :Thread-2 Incoming Word:Thread-2 Method toUpperCase Entered :Thread-2 Executed Display() Method - Temp Word:THREAD-2 *******Lock Released by Thread :Thread-2 *******Lock Acquired by Thread :Thread-3 Incoming Word:Thread-3 Method toUpperCase Entered :Thread-3 Executed Display() Method - Temp Word:THREAD-3 *******Lock Released by Thread :Thread-3 |
If we are removing the re-entrant lock object everywhere or remove the lock.lock and unlock methods,
we could see all the objects parallely executing on the single object not waiting for each other, however not thread safe.
In the above example, we could see until the thread release the lock, other thread cannot take the lock.
However in the below result, we could see all the threads are executing the blocks independently.
Example Result if we comment out lock.lock() and lock.unlock() methods in above example.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
*******No Lock Acquired by Thread :Thread-1 Incoming Word:Thread-1 Method toUpperCase Entered :Thread-1 *******No Lock Acquired by Thread :Thread-2 *******No Lock Acquired by Thread :Thread-0 Incoming Word:Thread-2 *******No Lock Acquired by Thread :Thread-3 Incoming Word:Thread-3 Method toUpperCase Entered :Thread-2 Incoming Word:Thread-0 Method toUpperCase Entered :Thread-3 Method toUpperCase Entered :Thread-0 Executed Display() Method - Temp Word:THREAD-3 *******No Lock Released by Thread :Thread-3 Executed Display() Method - Temp Word:THREAD-0 *******No Lock Released by Thread :Thread-0 Executed Display() Method - Temp Word:THREAD-1 *******No Lock Released by Thread :Thread-1 Executed Display() Method - Temp Word:THREAD-2 *******No Lock Released by Thread :Thread-2 |