【问题标题】:Why ReentrantLock algorithm from book is not working?为什么书中的 ReentrantLock 算法不起作用?
【发布时间】:2020-04-29 21:46:16
【问题描述】:

今天我读了Nir Shavit, Maurice Herlihy, The Art of Multiprocessor Programming 并遇到了一件非常难以理解(对我个人而言)的事情。

所以,我在 java 中找到了 ReentrantLock 的实现(对我来说是第 188 页,第 8 章):

class SimpleReentrantLock implements Lock {

    Lock lock;
    Condition condition;
    long owner, holdCount;

    SimpleReentrantLock() {
        lock = new SimpleLock();
        condition = lock.newCondition();
        owner = 0;
        holdCount = 0;
    }

    @Override
    public void lock() {
        long me = Thread.currentThread().getId();
        lock.lock();
        if (owner == me) {
            holdCount++;
            return;
        }

        while (holdCount != 0) {
            try {
                condition.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        owner = me;
        holdCount = 1L;
    }

    @Override
    public void unlock() {
        lock.lock();
        try {
            if (holdCount == 0 || owner != Thread.currentThread().getId()) {
                throw new IllegalMonitorStateException();
            }

            holdCount--;
            if (holdCount == 0) {
                condition.signal();
            }
        } finally {
            lock.unlock();
        }
    }
    // Other methods for Lock interface...
}

我分析了这段代码,仍然没有完全理解。

所以,我可以通过这种方式使用java.util.concurrent.locks 中的ReentrantLock

lock.lock();
lock.lock();
// Some code here...
lock.unlock();
lock.unlock();

而且没问题,因为是ReentrantLock,可以多次获取临界区。

例如,您可以从本书中找到自旋锁实现:

class TASLock implements Lock {
    private AtomicBoolean state = new AtomicBoolean(false);

    @Override
    public void lock() {
        while(state.getAndSet(true));
    }

    @Override
    public void unlock() {
        state.set(false);
    }

    // Other Lock methods...
}

此实现按预期工作。

所以,你可以从SimpleReentrantLock 注意到下一件事:

 lock = new SimpleLock();

正如作者告诉我们的那样:

我们将内部锁字段初始化为一个(虚构的)SimpleLock 类的对象,该类可能是不可重入的

但实际上,我已经实现了不可重入锁 (TASLock),所以我将进行下一个内联:

lock = new TTASLock();

最后,当我尝试执行下一个代码时,我会遇到死锁:

new Thread(() -> {
            lock.lock();
            lock.lock();
            System.out.println("No deadlock found.");
            lock.unlock();
            lock.unlock();
}).start();

而且看起来很清楚,因为在lock 方法中我们有这样的代码:

 lock.lock();

我们实际上是在没有任何先决条件的情况下尝试在同一个锁对象上两次获取临界区。

书中是否指出了错误的算法?还是我没听懂?

【问题讨论】:

  • 现在你的问题是什么?
  • @RavindraRanwala 我的问题:书中是否指出了错误的算法?还是我没听懂?
  • 因为在本书中,这种互斥体算法被称为“可重入”,正如我所见,实际上并非如此。所以,我有点困惑,这就是我问这个问题的原因。
  • 为什么说它不可重入或不工作?你有什么证据?
  • @RavindraRanwala 可重入锁如果您将在同一个互斥锁上多次获取它,则不会出现死锁。所以,请检查我的例子。无论如何,您可以自己测试它,因为我共享了代码(来自书本)。

标签: java multithreading concurrency


【解决方案1】:

lock() 方法中缺少 lock.unlock()。算法是正确的,只是一个简单的疏忽。
如书中所述:

因为这两个字段是原子操作的,所以我们需要一个内部短期锁。

为了回答评论中的问题,这里是lock()的更正版本:

    public void lock() {
    long me = Thread.currentThread().getId();
    lock.lock();
    try{
    if (owner == me) {
        holdCount++;
        return;
    }

    while (holdCount != 0) {
        try {
            condition.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    owner = me;
    holdCount = 1L;
    } finally {        
        lock.unlock();  // this call is missing
    }
}

【讨论】:

  • 你能告诉我这个解锁在这个代码中遗漏的地方吗?
  • 我把这个添加到anwser
  • 所以,它看起来像是书中的错字。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-05-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多