活锁还是死锁?

时间:2014-07-07 14:46:15

标签: java multithreading livelock

我正在准备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);
    }
}

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

4 个答案:

答案 0 :(得分:3)

根据维基百科的定义,我不会将其视为 livelock

  

活锁类似于死锁,除了状态   活锁中涉及的过程不断变化   彼此没有进步。

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

  

在操作系统中,死锁是指a时发生的情况   进程或线程因请求的资源而进入等待状态   正在等待另一个等待过程的等待过程   另一种资源。如果进程无法更改其状态   无限期地因为它所请求的资源正被使用   另一个等待过程,然后系统被认为陷入僵局。

你的第一个帖子锁定了obj1,第二个锁定了obj2,然后他们请求另一个锁定和阻止。

答案 1 :(得分:1)

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

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

启动线程后调用System.out.println(t*.getState()),打印: TIMED_WAITING

display方法 AFTER Thread.sleep(5)中相同,打印: RUNNABLE

现在关键部分:

再次在System.out.println(t*.getState())中调用t1(对于t2main两个帖子),但这次sleep(5000)将打印 BLOCKED

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

答案 2 :(得分:0)

以下是每个线程的工作原理:

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

    在第1阶段请求锁定之前,未释放步骤3中的锁定。对我而言,这是一个潜在的死锁,因为如果两个线程都在其他命中1之前通过阶段3,则两个线程都会锁定。但是,由于两个线程实际上可以正常工作,只要它们从未击中它,然后可能可以将其解释为活锁,因为两个线程可以愉快地一起运行一段时间在他们最终致命拥抱之前。

    所以我称之为潜在的僵局,并让小贩决定将其称为什么。

答案 3 :(得分:0)

超越程序并获得线程转储
第一个帖子

"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)

看起来像死锁情况。