Живой замок или тупик?

1

Я готовлюсь к экзамену Java SE 7 Programmer II. В одном из макетов экзаменов было упражнение, чтобы назвать, с какой проблемой потоковой передачи страдает код. Это код:

public class Test {

    public static void main(String[] args) {
        final Counter obj1 = new Counter("obj1");
        final Counter obj2 = new Counter("obj2");

        new Thread(new Runnable() {
            @Override
            public void run() {
                Thread.currentThread().setName("first");
                obj1.display(obj2);
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                Thread.currentThread().setName("second");
                obj2.display(obj1);
            }
        }).start();
    }
}

class Counter extends Thread {
    int i = 10;
    String name;

    public Counter(String name) {
        this.name = name;
    }

    public synchronized void display(Counter obj) {
        try {
            Thread.sleep(5);
            obj.increment(this);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public synchronized void increment(Counter obj) {
        System.out.println(++i);
    }
}

Говорят, что это живое существо. Я не вижу этого. Пожалуйста, кто-нибудь может объяснить это более подробно.

Теги:
multithreading
livelock

4 ответа

3

Я бы не квалифицировал это как livelock на основе определения Википедии

Жилище похоже на тупик, за исключением того, что состояния процессов, вовлеченных в livelock, постоянно меняются друг относительно друга, ни одна из них не прогрессирует.

Хотя это соответствует определению тупика

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

В первом потоке есть блокировка для obj1, вторая имеет блокировку obj2, затем запрашивает другую блокировку и блокировку.

1

После проверки состояния потоков я теперь уверен, что это тупик!

Я сохранил потоки в locals t1 и t2, чтобы вызвать метод getState().

System.out.println(t*.getState()) сразу после запуска потоков, печатает: TIMED_WAITING.

То же самое в методе display ПОСЛЕ Thread.sleep(5), печатает: RUNNABLE

И теперь ключевая часть:

Вызов System.out.println(t*.getState()) (для обоих потоков t1 и t2) в main снова, но на этот раз после sleep(5000) будет напечатано BLOCKED.

Заблокированные средства: ожидание блокировки, а это означает, что это DEADLOCK!

0

Ran выше программы и получил дамп потока
для первой нити

"first@573" prio=5 tid=0xb nid=NA waiting for monitor entry
  java.lang.Thread.State: BLOCKED
     blocks second@575
     waiting for second@575 to release lock on <0x245> (a dp.Counter)
      at dp.Counter.increment(LiveLock.java:44)
      at dp.Counter.display(LiveLock.java:37)
      - locked <0x246> (a dp.Counter)
      at dp.LiveLock.lambda$main$0(LiveLock.java:15)
      at dp.LiveLock$$Lambda$1.16460856.run(Unknown Source:-1)
      at java.lang.Thread.run(Thread.java:745)

и для второй нити

"second@575" prio=5 tid=0xc nid=NA waiting for monitor entry
  java.lang.Thread.State: BLOCKED
     blocks first@573
     waiting for first@573 to release lock on <0x246> (a dp.Counter)
      at dp.Counter.increment(LiveLock.java:44)
      at dp.Counter.display(LiveLock.java:37)
      - locked <0x245> (a dp.Counter)
      at dp.LiveLock.lambda$main$1(LiveLock.java:20)
      at dp.LiveLock$$Lambda$2.23661220.run(Unknown Source:-1)
      at java.lang.Thread.run(Thread.java:745)

выглядит как тупиковая ситуация.

0

Вот как работает каждый поток:

  1. Получить блокировку на одном объекте - происходит в методе run потока, потому что Counter.display synchronized.
  2. Вызовите его методом display.
  3. Получить блокировку на другом объекте - подготовка вызова метода increment другого объекта, поскольку метод increment synchronized.
  4. Вызовите другой прирост и попросите его увеличить свой собственный объект - странный, но не способный к взаимоблокировке.

Помимо вопроса о том, что вы не устанавливаете i volatile, потому что к нему можно получить доступ в многопоточном режиме, здесь есть ряд проблем, связанных с блокировкой, но главный из них:

Блокировка, удерживаемая на шаге 1, не освобождается до того, как блокировка запрошена на этапе 3. Для меня это потенциальный тупик, потому что оба потока будут заблокированы, если они оба пройдут этап 1 перед другими нажатиями 3. Однако, поскольку возможно, что эти два потока действительно работают нормально, пока они никогда не попадают в это, тогда может быть возможно интерпретировать это как livelock, поскольку эти два потока могут с радостью работать вместе в течение некоторого времени, прежде чем они в конечном итоге станут смертоносными.

Поэтому я бы назвал этот потенциальный тупик и позволил педантам решить, как это назвать.

  • 0
    Спасибо, я с вами согласен!

Ещё вопросы

Сообщество Overcoder
Наверх
Меню