线程、锁和条件的状态

State of threads, locks, and conditions

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

如果线程t1使用完锁,t2会自动切换回runnable还是需要使用notifyAll()?没有条件的 ReentrantLock 用法呢?如果您没有使用条件,您如何通知线程 t2 它应该切换回可运行状态?不带条件地使用可重入锁是否明智,甚至可能?

如果这个问题已经得到解答(我找不到),请link发给我。

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

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

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

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

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

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

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