void lockInterruptibly () - PullRequest
       66

void lockInterruptibly ()

0 голосов
/ 30 сентября 2019

Я пытаюсь понять метод lockInterruptibly. До сих пор я понял: void lockInterruptibly () -> получает блокировку, если она доступна, и выполняет обычные операции. Если блокировка недоступна, то поток переходит в состояние ожидания и во время ожидания объекта блокировки, если поток прерывается, он не получит объект блокировки. Если оно не прервано, то только оно получит блокировку.

Я пытался понять из Java docs API , однако мне это не очень понятно. Может ли кто-нибудь выразить это другими словами?

Также я не могу понять, как ниже приведен возможный результат для этой программы?

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

Программа: -

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....");
    }
}

Последние синхронизированные блоки synchronized(l) в test1() and test2() методы класса Test выполняются, поскольку они содержат монитор в l (как используемое синхронизированное ключевое слово) илиблокировка l (как потоки называются l.lockInterruptibly();) перед входом в синхронизированные блоки?

Причина, по которой я спрашиваю, удерживает ли она монитор l или блокировка l, заключается в том, что в примере ниже:

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();
}
}

Несмотря на то, что main поток содержит lock of object m(m.lock()), но он дает IllegalMonitorStateException , поскольку в нем нет monitor of object m.

Надеюсь, мой вопрос легче понять.

Ответы [ 2 ]

3 голосов
/ 30 сентября 2019

Я пытался понять из API Java docs, но мне это не очень понятно. Может ли кто-нибудь выразить это другими словами?

Простая версия: l.lockInterruptibly() делает то же самое, что и l.lock(), за исключением того, что поток, ожидающий в l.lockInterruptibly(), немедленно ответит на прерывание, тогда какпоток, ожидающий в l.lock(), не может ответить до тех пор, пока не получит блокировку.


FWIW: причина в двух разных способах блокировки мьютекса , вероятно, (я догадываюсь), что ваша программа снижает производительность, вызывая lockInterruptibly вместо вызова lock.

Но обратите внимание! Есть большая причина, почему вы должны избегать lockInterruptibly. То есть, если ваша программа когда-либо удерживает блокировку достаточно долго, чтобы вы заботились о том, прерываемая ли она или нет, то, вероятно, существует более разумный способ координировать активность потоков вашей программы. Вы должны стараться никогда не блокировать блокировку дольше, чем требуется для назначения нескольких переменных.

2 голосов
/ 30 сентября 2019

Первое, на что следует обратить внимание, это то, что когда поток прерывается в Java, все, что происходит (эффективно), - это флаг, установленный в этом потоке. Это зависит от кода, выполняющегося в самом потоке, чтобы проверить этот флаг, вызвав Thread.interrupted(). Единственное небольшое исключение из этого - случай, когда протектор входит в состояние прерывистого сна или ожидания. В этом случае:

  • InterruptedException генерируется немедленно , если флаг прерывания уже установлен
  • InterruptedException также генерируется, если потокпрервано во время ожидания

Обратите внимание, что когда InterruptedException выбрасывается ожиданием или сном, или когда вызывается Thread.interrupted(), флаг прерывания потока будет автоматически сброшен.

Имея это в виду, вот схема последовательности , чтобы объяснить, как может произойти ваш первый "возможный вывод":

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...."
--
|
*

Второй вывод довольнопочти так же, кроме порядка нескольких последних отпечатков.

Итак, вот моя альтернативная формулировка поведения lockInterruptibly():

  • Во-первых, выдается InterruptedException, еслив текущем потоке имеется необработанное прерывание
  • Проверяет, доступна ли блокировка
  • Если нет, ожидает его доступности или прерывания потока
  • Еслипоток прерывается до того, как блокировка становится доступной, выбрасываетInterruptedException
  • Возвращает (блокировка теперь удерживается вызывающим потоком)
...