Почему метод receiveUninterruptibly () класса Semaphore в java не работает должным образом? - PullRequest
2 голосов
/ 28 января 2020

У меня есть два Java файла:

Проверка. java

import java.util.concurrent.Semaphore;
class Check
{
 public static void main(String[] args)
 {
  Semaphore s = new Semaphore(1);
  MyThread t1 = new MyThread("t1",s);
  MyThread t2 = new MyThread("t2",s);
  t1.start();
  t2.start();
  t2.interrupt();
 }
}

MyThread. java

import java.util.concurrent.Semaphore;
class MyThread extends Thread
{
 Semaphore s;
 MyThread(String name, Semaphore s)
 {
  super(name);
  this.s=s;
 }
 public void run()
 {
  try
  {
        s.acquireUninterruptibly();
        for(int i=0; i<5; i++)
        {
                Thread.sleep(500);
                System.out.println(Thread.currentThread().getName()+"-"+i);
        }
        s.release();
  }
  catch(InterruptedException e){}
 }
}

Если я закомментирую оператор "t2.interrupt ()", тогда оба потока выполняются нормально. Но если я не прокомментирую утверждение, то поток t2 вообще не выполняется. Согласно моему пониманию метода acquUninterruptibly () поток t2 должен продолжать ожидать разрешения даже после получения прерывания. Тогда почему поток t2 перестает работать после получения прерывания?

Ответы [ 2 ]

4 голосов
/ 05 февраля 2020

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

При изменении кода на

Semaphore s = new Semaphore(1);
Runnable r = () -> {
    System.out.println(Thread.currentThread().getName()+" - before acquire");
    s.acquireUninterruptibly();
    System.out.println(Thread.currentThread().getName()+" - acquired");
    for(int i=0; i<5; i++) {
        try {
            Thread.sleep(500);
        }
        catch(InterruptedException e){
            System.out.println(Thread.currentThread().getName()+" - "+e);
        }
        System.out.println(Thread.currentThread().getName()+" - "+i);
    }
    s.release();
};
Thread t1 = new Thread(r, "t1");
Thread t2 = new Thread(r, "t2");
t1.start();
t2.start();
t2.interrupt();

в большинстве случаев вы получите

t1 - before acquire
t2 - before acquire
t1 - acquired
t1 - 0
t1 - 1
t1 - 2
t1 - 3
t1 - 4
t2 - acquired
t2 - java.lang.InterruptedException: sleep interrupted
t2 - 0
t2 - 1
t2 - 2
t2 - 3
t2 - 4

, Это не гарантируется, поскольку возможно, что t2 сначала получит семафор.

Для типичного времени t1 получает сначала, а t2 прерывается до или во время выполнения acquireUninterruptibly(). Но не имеет значения, прерывается ли t2 до, между или после вызова acquireUninterruptibly(), так как этот метод игнорирует прерывание. Поэтому состояние «прервано» сохраняется до тех пор, пока поток не выполнит первое действие, которое заботится о нем.

В этом примере это первый вызов sleep. В вашем коде это завершает поток, так как ваш оператор try … catch охватывает весь код. В моем примере он охватывает только один вызов sleep, и, поскольку этот вызов сбросит статус «прерванный» при сообщении о нем через InterruptedException, последующие вызовы будут переведены в спящий режим, как и предполагалось, после перехвата исключения.

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

Semaphore s = new Semaphore(1);
Runnable r = () -> {
    System.out.println(Thread.currentThread().getName()+" - before acquire");
    s.acquireUninterruptibly();
    System.out.println(Thread.currentThread().getName()+" - acquired");
    if(Thread.interrupted())
        System.out.println(Thread.currentThread().getName()
                          +" - interruption before or during acquire");
    for(int i=0; i<5; i++) {
        try {
            Thread.sleep(500);
        }
        catch(InterruptedException e){
            System.out.println(Thread.currentThread().getName()+" - "+e);
        }
        System.out.println(Thread.currentThread().getName()+" - "+i);
    }
    s.release();
};
Thread t1 = new Thread(r, "t1");
Thread t2 = new Thread(r, "t2");
t1.start();
t2.start();
t2.interrupt();

, что может привести к

t1 - before acquire
t2 - before acquire
t1 - acquired
t1 - 0
t1 - 1
t1 - 2
t1 - 3
t1 - 4
t2 - acquired
t2 - interruption before or during acquire
t2 - 0
t2 - 1
t2 - 2
t2 - 3
t2 - 4

Как уже говорилось, это не гарантируется. Иногда вместо этого выводится

t2 - before acquire
t1 - before acquire
t2 - acquired
t2 - interruption before or during acquire
t2 - 0
t2 - 1
t2 - 2
t2 - 3
t2 - 4
t1 - acquired
t1 - 0
t1 - 1
t1 - 2
t1 - 3
t1 - 4

.

Сравните с документацией Semaphore.acquireUninterruptibly()

Если текущий поток прерван в ожидании разрешения он будет продолжать ждать, но время, в которое потоку назначено разрешение, может измениться по сравнению со временем, в которое он получил бы разрешение, если бы не было прерывания. Когда поток возвращается из этого метода, его состояние прерывания будет установлено.

Последнее предложение является критической точкой. Когда метод прерывается, он будет продолжать ждать и возвращаться в обычном режиме, но «состояние прерывания потока» будет установлено, что заставит реагировать на него следующее чувствительное к прерыванию действие.

В отличие от этого , Thread.sleep:

Броски:


InterruptedException - если какая-либо нить прервала текущую нить. статус прерывания текущего потока очищается при возникновении этого исключения.

0 голосов
/ 28 января 2020

Ваш второй поток фактически начинает работать, НО, поскольку вы игнорируете InterruptedException (пустой блок catch для него), вы не можете обнаружить, что ваш вызов interrupt() выполнен, пока поток 2 находится в спящем методе. И этот метод выдает InterruptdeException, если прерван. И из-за этого исключения ваш поток 2 выходит из -l oop, ничего не печатая.

Это то, что я вижу после добавления e.printStackTrace() внутри блока catch.

t1- 0

t1-1

t1-2

t1-3

t1-4

java .lang.InterruptedException : сон прерывается в java .lang.Thread.sleep (собственный метод) в MyThread.run (Parser. java: 31)

...