【问题标题】:ReentrantLock.lock() doesn't block other threadsReentrantLock.lock() 不会阻塞其他线程
【发布时间】:2014-04-03 16:49:59
【问题描述】:

我很难理解 ReentrantLock.lock() 的行为

我有以下课程

import java.util.concurrent.locks.*;

class BlockingClass {

    private Lock lock = new ReentrantLock();

    private Condition condition = lock.newCondition();

    public void a() {
        lock.lock();
        System.out.println("called in method a(): lock()");

        try {
            System.out.println("called in method a(): await()");
            condition.await();
        } 
        catch (InterruptedException e) {} 
        finally {
            lock.unlock();
            System.out.println("called in method a(): unlock() ");
        }
    }

    public void b() {
        lock.lock();
        System.out.println("called in method b(): lock()");

        System.out.println("called in method b(): signalAll()");
        condition.signalAll();

        lock.unlock();
        System.out.println("called in method b(): unlock() ");
    }
}

我使用以下测试运行:

class BlockingClassTest {
    public static void main(String[] args) throws InterruptedException {

        final BlockingClass blockingClass = new BlockingClass();

        new Thread() {
            public void run() {
                System.out.println("Thread1 calling a()");
                blockingClass.a();
            }
        }.start();

        Thread.sleep(1000); 

        new Thread() {
            public void run() {
                System.out.println("Thread2 calling b()");
                blockingClass.b();
            }
        }.start();
    }
}

我预计会出现僵局。一旦 a() 方法调用 lock.lock(),我希望任何调用 b() 方法的人都必须在 b 的 lock.lock() 处等待,直到调用 a() 的线程调用 lock.unlock()。但是由于 a() 正在等待 b() 调用 condition.signalAll(),所以这两个方法都应该永远处于阻塞状态。

相反,这是我在控制台中得到的输出:

Thread1 calling a()
called in method a(): lock()
called in method a(): await()
Thread2 calling b()
called in method b(): lock()
called in method b(): signalAll()
called in method a(): unlock() 
called in method b(): unlock() 

我对 lock() 和 unlock() 的正确使用和功能有什么误解?

【问题讨论】:

    标签: java multithreading concurrency reentrantlock


    【解决方案1】:

    答案已经给出,但我想我只是引用javadocs for Condition.await() 来提供更多上下文:

    使当前线程等待,直到它收到信号或中断。

    与此条件关联的锁被自动释放,当前线程出于线程调度目的而被禁用并处于休眠状态,直到发生以下四种情况之一:

    1. 某个其他线程为此Condition调用了signal方法,而当前线程恰好被选为要被唤醒的线程;或
    2. 其他一些线程为此条件调用 signalAll 方法;或
    3. 其他线程中断当前线程,支持中断线程挂起;或
    4. 发生“虚假唤醒”。

    在所有情况下,在此方法可以返回之前,当前线程必须重新获取与此条件关联的锁。当线程返回时,它保证持有这个锁。

    因此,当您调用condition.await() 时,它会释放锁定,允许其他线程进入锁定部分。这与 Object.wait()synchronized 代码块内时的行为相同。

    【讨论】:

    • 感谢您对Object.wait() 的引用。这也很有帮助。
    【解决方案2】:

    您对condition.await(); 的调用将释放锁,使线程处于wait 状态,因此线程b 可以获取锁。

    您的a() 方法将在b() 释放它的锁定后继续运行,因为您已发出条件信号。

    【讨论】:

    • 你如何解释a() unlock()发生在b() unlock()之前的事实?
    • 这是您代码中的竞争条件。 System.out.println("..."); 方法被并发调用,因为不再获取锁。
    • ^ 您可以在 unlock() 调用之前打印输出,以更准确地查看操作顺序,@SotiriosDelimanolis
    【解决方案3】:

    你没有误会ReentrantLock,你误会了ConditionCondition 绑定到锁,Condition.await() 将有效地解锁、检查并等待,然后重新锁定锁。见Condition.await()

    a() 中,在lock() 和对await() 的调用之间,以及在await()unlock() 的返回之间,您的锁的行为与您预期的一样。在对await() 的调用中,Condition 正在管理它。

    这是“条件变量”一般概念的一部分;这就是为什么您发现的任何线程库都将某种锁与条件相关联(例如,在 POSIX C 中,pthread_cond_wait 需要条件变量互斥锁)。

    查看condition variables 上的维基百科文章,它详细解释了这种行为及其原因。

    【讨论】:

    • 感谢@ortang 和 Jason C 的出色而快速的回答。我接受这个只是因为它的完整性和有用的文档。
    猜你喜欢
    • 2013-07-11
    • 1970-01-01
    • 2016-03-27
    • 1970-01-01
    • 2012-11-08
    • 2017-02-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多