-2

I am trying to understand lockInterruptibly method. What I have understood so far is: void lockInterruptibly() --> acquires the lock if available and performs normal operations. If lock is not available then thread goes in waiting state and while waiting for the lock object if thread gets interrupted it won't get the lock object. If it is not interrupted then only it will get the lock.

I tried to understand from Java docs API however it's not very clear to me. Could anybody express it in other words?

Also I am unable to understand how come below is the possible output for this program?

Possible output 1)

Before interrupt Hello....
After t1.interrupt Hello and before t2.start()....
exception caught test1
After t2.start Hello but before t2.interrupt....
After t2.interrupt Hello....
exception caught test2
Main Hello....
Main Hello....
Main Hello....

Possible output 2)

Before interrupt Hello....
After t1.interrupt Hello and before t2.start()....
exception caught test1
After t2.start Hello but before t2.interrupt....
After t2.interrupt Hello....
Main Hello....
Main Hello....
Main Hello....
exception caught test2

Program: -

import java.util.concurrent.locks.*;
class Test{
    static Lock1 l = new Lock1(); 
    public void test1() throws InterruptedException{
        l.lockInterruptibly();
        synchronized(l){
        for(int i=0; i<100; i++){
           System.out.println(Thread.currentThread().getName() +" : " + i);
           try{
             Thread.sleep(100);
           }
           catch(InterruptedException e){
            System.out.println(Thread.currentThread().getName() + " got interrupted");     
           }
         }
      }
   }
    public void test2() throws InterruptedException{
        l.lockInterruptibly();
        synchronized(l){
        for(int i=100; i>0; i--){
          System.out.println(Thread.currentThread().getName() +" : " + i);
          try{
             Thread.sleep(100);
          }
          catch(InterruptedException e){
             System.out.println(Thread.currentThread().getName() + " got interrupted");     
          }
        }
      }
    }
 }

class MyThread1 extends Thread{
    Test t;
    MyThread1(String name, Test t){
        super(name);
        this.t = t;
    }
    public void run(){
      try{
      t.test1();
    }
    catch(InterruptedException e){
      System.out.println("exception caught test1");    
    }
  }
}

class MyThread2 extends Thread{
    Test t;
    MyThread2(String name, Test t){
      super(name);
      this.t = t;
    }
    public void run(){
     try{
     t.test2();
    }
    catch(InterruptedException e){
     System.out.println("exception caught test2");    
    }
   }
}
class Lock1 extends ReentrantLock{
}
public class Main{
    public static void main(String[] args) throws InterruptedException {
        Test t = new Test();
        MyThread1 t1 = new MyThread1("thread1",t);
        MyThread2 t2 = new MyThread2("thread2",t);
        t1.start();
        System.out.println("Before interrupt Hello....");
        t1.interrupt();
        System.out.println("After t1.interrupt Hello and before t2.start()....");       
        t2.start();
        System.out.println("After t2.start Hello but before t2.interrupt....");
        t2.interrupt();
        System.out.println("After t2.interrupt Hello....");
        System.out.println("Main Hello....");
        System.out.println("Main Hello....");
        System.out.println("Main Hello....");
    }
}

Lastly synchronized blocks synchronized(l) in test1() and test2() methods of class Test are executed as they are holding the monitor of l (as used synchronized keyword) or lock of l (as threads called l.lockInterruptibly();) before entering synchronized blocks?

The reason why I ask if it is holding monitor of l or lock of l is because in below example:

import java.util.concurrent.locks.*;

class Lock1 extends ReentrantLock{
}

class Main{
public void test() throws InterruptedException{
synchronized(this){
Lock1 m = new Lock1();    
System.out.println("line 1");
m.lock();
System.out.println(m.getHoldCount());
System.out.println(m.isHeldByCurrentThread());
System.out.println("line 2");
m.wait();
System.out.println("line 3");
}
}
public static void main(String[] args) throws InterruptedException{
Main t1 = new Main();
t1.test();
}
}

Even though main thread is holding lock of object m(m.lock()) but it gives IllegalMonitorStateException because it doesn't have monitor of object m.

Hope my question is easier to understand.

raven03
  • 89
  • 6
  • 1
    Using `Lock` is an _alternative_ to using `synchronized`. Also, you never unlock the `Lock`. As shown in the [class documentation](https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/util/concurrent/locks/Lock.html), the standard idiom is to use `lock.lock(); try { /* guarded code */ } finally { lock.unlcok(); }`. – Slaw Sep 30 '19 at 11:18
  • @Slaw please respond to my complete question? – raven03 Sep 30 '19 at 11:31
  • 1
    @raven03, the flaws that Slaw called out are _serious_. You should fix those first, and then see if you still have questions. – Solomon Slow Sep 30 '19 at 12:43
  • @SolomonSlow, thanks for your recommendation but the program I have shared is for educational purpose to understand the concept of l.lockInterruptibly() and synchronized block. So it would be great if you could please provide answer to the questions that I have asked. – raven03 Sep 30 '19 at 13:03
  • 2
    It is easier to ask questions than to understand the answers. try to slow down and absorb some of what you have been getting told, rather than keep posting questions with the same kinds of mistakes. – Nathan Hughes Sep 30 '19 at 13:16
  • @NathanHughes, I have spent enough time researching before asking this question. And I think my questions are valid enough to be answered on this forum. Can you help me answer my last question? Lastly synchronized blocks synchronized(l) in test1() and test2() methods of class Test are executed as they are holding the monitor of l (as used synchronized keyword) or lock of l (as threads called l.lockInterruptibly()) before entering synchronized blocks? I believe that threads are holding monitor of lock as synchronized keyword is used and lockInterruptibly() has nothing todo with sync block? – raven03 Sep 30 '19 at 13:39
  • 2
    The posted code mixing implicit locks with Reentrant locks is nonsensical. I suggest you try absorbing the repeated advice you've been getting about that. – Nathan Hughes Sep 30 '19 at 13:48
  • @NathanHughes and Solomon Slow -- At docs.oracle.com/en/java/javase/13/docs/api/java.base/java/util/… for lockInterruptibly() it states `Acquires the lock unless the current thread is interrupted. Acquires the lock if it is available and returns immediately.` What does it mean by **returns immediately**?? – raven03 Sep 30 '19 at 14:09
  • Hopefully, you know what "returns" means. "Immediately" means, without blocking the calling thread and, without causing any side effects. – Solomon Slow Sep 30 '19 at 14:43
  • The documentation is telling you that when a thread enters that method it will do one of the following: (1) If the thread is interrupted, throw an `InterruptedException`; (2) If the thread is not interrupted and the lock is currently available, acquire the lock and return from the method; (3) If the thread is not interrupted and the lock is not available, block until either the lock becomes available and can be acquired or the thread is interrupted—in the latter case, an `InterruptedException` is thrown. – Slaw Sep 30 '19 at 15:22
  • Thanks for your comments. But I would like to understand why my questions have been down voted? – raven03 Oct 01 '19 at 00:43
  • 1
    Most likely people find your question unclear. At times you seem focused on how `lockInterruptibly()` works contractually. Other times you seem to have a fundamental misunderstanding of how either `synchronized` or `Lock` work conceptually; case in point, your examples mix the two together despite them having nothing to do with each other beyond the concept of _mutual exclusion_. Then you have a subclass, `Lock1`, whose purpose is unknown—why not use `ReentrantLock` directly? Also, the code is not formatted properly. All this makes it difficult to determine what you're actually asking about. – Slaw Oct 01 '19 at 13:34
  • Thanks guys. I am new to this platform and not sure how to format the code so it is easier for people to understand and respond. I did research before posting a question and tried to follow tips to make questions easier for the audience but it seems I still lack at it. – raven03 Oct 04 '19 at 10:20

2 Answers2

3

I tried to understand from Java docs API however it's not very clear to me. Could anybody express it in other words?

The simple version is, l.lockInterruptibly() does the same thing as l.lock() except that a thread waiting in l.lockInterruptibly() will immediately respond to an interrupt, whereas a thread waiting in l.lock() can not respond until after it has acquired the lock.


FWIW: The reason there are two different ways to lock a mutex probably is (I'm guessing) that your program takes a performance hit by calling lockInterruptibly instead of calling lock.

But note! There's a bigger reason why you should avoid lockInterruptibly. That is, if your program ever keeps a lock locked long enough that you care whether it's interruptible or not, then there probably is a smarter way to coordinate the activity of your program's threads. You should strive to never keep a lock locked for any longer than the time it takes to assign a few variables.

Solomon Slow
  • 25,130
  • 5
  • 37
  • 57
3

The first thing to note is that when a thread in interrupted in Java, all that happens (effectively) is a flag is set on that thread. It is up to code running in the thread itself to check this flag by calling Thread.interrupted(). The only slight exception to this is if the tread enters an interruptible sleep or wait state. In that case:

  • An InterruptedException is thrown immediately if the interrupted flag is already set
  • An InterruptedException is also thrown if the thread is interrupted during the wait

Note that when an InterruptedException is thrown by a wait or sleep, or when Thread.interrupted() is called, the thread's interrupted flag will be automatically unset.

With that in mind, here is a noddy sequence diagram to explain how your first "possible output" might occur:

Main                  T1                                T2
|                     .                                 .
--                    .                                 .
Create objects        .                                 .
--                    .                                 .
|                     .                                 .
--                    .                                 .
Start T1 |----------->*                                 .
--                    |                                 .
|                     |                                 .
--                    |                                 .
Print "Before..."     |                                 .
--                    |                                 .
|                     |                                 .
--                    |                                 .
Interrupt T1 |------->|                                 .
--                    --                                .
|                     Enter lockInterruptibly()         .
|                      -Check for interrupt (found)     .
|                      -Throw InterruptedException      .
--                    --
Print "After t1..."   |
--                    --                                .
|                     Print "exception..."              .
|                     --                                .
|                     |                                 .
|                     *                                 .
--                                                      .
Start T2 |--------------------------------------------->*
--                                                      |
|                                                       |
--                                                      |
Print "After t2..."                                     |
--                                                      |
|                                                       |
--                                                      |
Interrupt T2 |----------------------------------------->|
--                                                      --
|                                                       Enter lockInterruptibly()
--                                                       -Check for interrupt (found)
Print "After t2..."                                      -Throw InterruptedException
--                                                      --
|                                                       |
|                                                       --
|                                                       Print "exception..."
--                                                      --
Print "Main Hello...."                                  |
--                                                      *
|
--
Print "Main Hello...."
--
|
--
Print "Main Hello...."
--
|
*

The second output is pretty much the same apart from the ordering of the last few prints.

So, here's my altenative wording for the behaviour of lockInterruptibly():

  • First, throws an InterruptedException if there's an unhandled interrupt on the current thread
  • Checks if the lock is available
  • If not, waits for it to become available, or for the thread to be interrupted
  • If the thread is interrupted before the lock becomes available, throws an InterruptedException
  • Returns (the lock is now held by the calling thread)
user31601
  • 2,482
  • 1
  • 12
  • 22
  • so just to make sure I got you correctly that if the thread interrupted flag was set before thread called l.lockInterruptibly() then even if when thread called l.lockInterruptibly() method and lock was available to be acquired, it will throw InterruptedException and will not acquire the lock? – raven03 Sep 30 '19 at 13:43
  • I believe so, yes – user31601 Sep 30 '19 at 13:45
  • I mean that is what you have tried to say in your above answer, right? – raven03 Sep 30 '19 at 13:46
  • Yes indeed it is – user31601 Sep 30 '19 at 13:47
  • I only said "I believe so" because I haven't actually checked the code of "lockInterruptibly" myself. However, this behaviour is consistent with the results you are seeing _and_ is the sensible way to behave. Good code should check for interrupts regularly - since `lockInterruptibly()` declares an `InterruptedException`, it _can_ perform an interrupt check without throwing information away, so it _should_, regardless of whether the lock is available. – user31601 Sep 30 '19 at 13:51
  • Otherwise, the interrupt might never be noticed. – user31601 Sep 30 '19 at 13:52
  • At https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/util/concurrent/locks/Lock.html for lockInterruptibly() it states `Acquires the lock unless the current thread is interrupted. Acquires the lock if it is available and returns immediately.` What does it mean by **returns immediately**?? – raven03 Sep 30 '19 at 14:05
  • `Acquires the lock unless the current thread is interrupted.` - Here _interrupted_ is a _state_ not an _action_. The 'unless' part of this sentence describes the first thing `lockInterruptibly` does, similar to my first bullet point. `Acquires the lock if it is available and returns immediately.` - this is my second bullet point. It happens _after_ the first sentence/first bullet point. _Returns immediately_ means it skips bullet point 3 and 4, and goes straight to 5. – user31601 Sep 30 '19 at 14:10
  • The javadoc you posted a link to also says `If the current thread:...`. The bullet points following that exactly describe the situation in which an `InterruptedException` can be thrown, which matches what I've been saying here. – user31601 Sep 30 '19 at 14:15
  • `If the current thread: has its interrupted status set on entry to this method; or is interrupted while acquiring the lock, and interruption of lock acquisition is supported, then InterruptedException is thrown and the current thread's interrupted status is cleared.` It also clears **so just to make sure I got you correctly that if the thread interrupted flag was set before thread called l.lockInterruptibly() then even if when thread called l.lockInterruptibly() method and lock was available to be acquired, it will throw InterruptedException and will not acquire the lock?** this doubt. – raven03 Sep 30 '19 at 14:20
  • Yes, that's it exactly – user31601 Sep 30 '19 at 14:51
  • Thanks buddy for all your responses. Could you please upvote this question? I think this question has lot of good responses for others to see in future. – raven03 Oct 01 '19 at 00:44
  • 2
    I'm afraid I agree with many of the comments below the question. While I've been happy to "read between the lines", and help with the issues I think you're seeing, the question as it's written will be confusing to readers looking to understand the behaviour of Locks in general and `lockInterruptibly` in particular. As others have mentioned, the mixing of Locks and `synchronized` blocks is of particular concern. Also, the code you've pasted takes a while to digest, and is a fair bit more complex than a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example). – user31601 Oct 01 '19 at 08:32
  • Programs should be simple. Simple programs are less likely to be broken, and they're easier to fix. Simple programs are easier to understand, and it's easier to get other people to help you with them. If thread A is interrupted, you should want it to respond simply and promptly to the interrupt. If it's not possible to understand your program without knowing whether thread A can sneak one more time through an uncontested `lockInterruptibly()` call before it handles a pending interrupt, then your program probably is more complicated than it needs to be. – Solomon Slow Oct 01 '19 at 14:11
  • Thanks guys. I am new to this platform and not sure how to format the code so it is easier for people to understand and respond. I did research before posting a question and tried to follow tips to make questions easier for the audience but it seems I still lack at it. – raven03 Oct 04 '19 at 10:20