关于java:Livelock还是Deadlock?

Livelock or a Deadlock?

我正在准备Java SE 7 Programmer II考试。 在其中的一次模拟考试中,有一项练习是从代码遭受的线程问题中指出的。 这是代码:

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

他们说这是一个活锁。 我看不到。 请有人可以详细解释。


根据维基百科的定义,我不认为这是活锁

A livelock is similar to a deadlock, except that the states of the
processes involved in the livelock constantly change with regard to
one another, none progressing.

虽然它确实符合死锁的定义

In an operating system, a deadlock is a situation which occurs when a
process or thread enters a waiting state because a resource requested
is being held by another waiting process, which in turn is waiting for
another resource. If a process is unable to change its state
indefinitely because the resources requested by it are being used by
another waiting process, then the system is said to be in a deadlock.

您的第一个线程具有obj1的锁,第二个线程具有obj2的锁,然后它们请求另一个线程的锁并阻塞。


检查线程状态后,我现在确定这是一个死锁!

我将线程存储在本地t1t2中,以便调用getState()方法。

在启动线程之后立即调用的System.out.println(t*.getState())打印:TIMED_WAITING。

Thread.sleep(5)之后,在display方法中相同,将打印:RUNNABLE

现在关键部分是:

再次在main中调用System.out.println(t*.getState())(对于两个线程t1t2),但是这次在sleep(5000)之后将打印BLOCKED。

被阻止意味着:等待获取锁,这意味着它是死锁!


运行以上程序并获得线程转储
对于第一个线程

1
2
3
4
5
6
7
8
9
10
"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)

和第二个线程

1
2
3
4
5
6
7
8
9
10
"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)

看起来像是僵局。


每个线程的工作方式如下:

  • 获取一个对象的锁-在线程的run方法中发生,因为Counter.displaysynchronized
  • 称为display方法。
  • 获得对另一个对象的锁定-准备调用另一个对象的increment方法,因为increment方法为synchronized
  • 调用另一个增量,并要求它递增自己的对象-奇怪,但不会导致死锁。
  • 除了不设置i volatile(因为可以通过多线程方式对其进行访问)以外,这里还存在许多与锁相关的问题,但主要问题是:

    在阶段3请求锁之前,不会释放在步骤1中持有的锁。对我来说,这是一个潜在的死锁,因为如果两个线程都在另一个命中3之前都经过了阶段1,则它们将锁定。但是,由于两个线程只要它们从未命中就可以实际正常工作,则有可能将其解释为一个活锁,因为两个线程可以在最终致命的拥抱之前愉快地运行一段时间。

    因此,我将这种潜在的僵局称为"僵局",让学徒决定如何称呼它。