【问题标题】:State of threads, locks, and conditions [closed]线程、锁和条件的状态[关闭]
【发布时间】:2017-05-17 23:11:32
【问题描述】:

在 Java 中,如果一个线程 t2 尝试从同步获取锁,该锁当前正被另一个线程 t1 使用,那么 t2 将从可运行切换到阻塞。正确的? ReentrantLocks 呢?

如果线程t1 完成使用锁,t2 会自动切换回可运行状态还是需要使用notifyAll()?在没有条件的情况下使用ReentrantLock 怎么样。如果您没有使用条件,您如何通知线程t2 它应该切换回可运行状态?无条件使用可重入锁是否明智,甚至可能?

如果这个问题已经得到解答(我找不到),如果你能将它链接到我,我将不胜感激。

【问题讨论】:

    标签: java multithreading locking synchronized reentrantlock


    【解决方案1】:

    听起来您混淆了阻塞状态和等待状态。阻塞意味着线程正在尝试获取锁并且不能因此被卡住。等待意味着线程处于休眠状态;它一直挂起,直到它收到通知,或者直到它从等待中回来(超时,如果调用超时值,或虚假唤醒)。

    一旦锁变得可用,操作系统调度程序必须决定哪个阻塞线程获得它。它选择获取锁的线程变为可运行的。

    因此,通知与等待线程有关,而不是与阻塞线程有关。一个拥有锁但发现它无法继续的线程(它检测到它正在等待的条件不正确)可以调用该锁的等待,释放锁并进入休眠状态。您使用 notify 来告诉调度程序唤醒任何一个正在等待锁的线程。一旦线程被唤醒,它必须重新获得之前释放的锁,然后才能退出等待方法。

    ReentrantLock 的基本行为类似于内在锁,不同之处在于您可以使用可重入锁有多个条件。请记住,ReentrantLock 有自己单独的调用方法(等待和信号,而不是等待和通知)。当您希望线程等待并获得通知时,您可以使用带有 ReentrantLock 的条件,使用不同的条件,以便线程仅在与它们相关的条件上等待。

    【讨论】:

    • 我认为他只是在使用“可运行”一词来表示“等待”。
    • 我的困惑来自我正在阅读的书(Cay S. Horstmann 的 Object-Orinted Design and Patterns)。在本书中,以下内容可以阅读Screenshot。这是简化还是我遗漏了什么?
    • @erikp 对我来说似乎是一种简化 - 请参阅 JavaDocs of Thread.State
    • @erikp 另见 JLS 的相关章节:Chapter 17. Threads and Locks - 阻塞在 17.1 中处理,而等待和通知在 17.2 中处理
    • @erik:此图可用于显示调度程序的角色。它确实简化了事情,我认为它并不是为了让您将其用作从一种状态转换到另一种状态的路线图,尽管我知道您如何获得这个想法。
    【解决方案2】:

    如果线程 t2 尝试在另一个线程 t1 正在使用的锁上进行同步 - 例如,当 t1 已经在同一锁上的同步块中时尝试进入同步块 - 那么 t2 将阻塞,是的。对于可重入锁也是如此,包括ReentrantLock 类;应该注意的是,默认锁在 Java 中是可重入的(稍后会详细介绍)。

    如果 t1 释放默认锁,例如通过退出同步块,则 t2 被解除阻塞;这是语言的一个特点。但是,如果您使用 ReentrantLock,则持有锁的线程必须显式调用 ReentrantLock.unlock() 来释放锁,就像它必须调用 ReentrantLock.lock() 来获取锁一样。

    注意,“可重入”是指单个线程是否可以“重入”同步块,而不是线程之间的任何交互。重入锁可以被已经持有锁的线程再次加锁;不可重入锁不能。请注意,在 Java 中,如果单个线程多次获得可重入锁,则它必须释放相同次数的锁,然后其他等待锁的线程才会被解除阻塞。对于默认锁,嵌套同步块自然会发生这种情况,可能在不同的函数调用级别。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-11-04
      • 2013-02-03
      • 2010-11-08
      • 2011-05-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多