为什么等待条件释放锁定但信号没有?
我编写下面的代码来测试线程在等待Condition
对象时何时处于唤醒Condition
。
- 但是我发现在调用
signal()
之后我必须解锁。 此方法不会释放锁定,而await()将释放此锁定。
这是来自条件#等待
与此条件关联的锁被primefaces释放 ,并且当前线程因线程调度而被禁用,并处于hibernate状态,直到发生以下四种情况之一:
这是来自Conditon#signal
唤醒一个等待线程。
如果有任何线程在这种情况下等待,则选择一个线程进行唤醒。 然后该线程必须在从await返回之前重新获取锁。
但在我的代码中,这是不正确的,直到我们解锁。 为什么这样设计? 因为在我看来,当我们决定向其他人发出信号时,我们不应再持有锁,我错了吗?
-
既然我们可以在调用
signal
和unlock
之间做很多事情,比如说我睡了10秒,那么java信号的另一个线程到底是什么时候? 还有另一个背景线程在我们发signal
和unlock
之间工作吗?public class WorkerThread extends Thread{ @Override public void run() { Monitor.lock.lock(); while (!Monitor.isConditionTrue){ try { Monitor.condition.await(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("THREAD ID "+this.getId()+"-------working --------"); System.out.println("------singnall--------"); Monitor.isConditionTrue=true; Monitor.condition.signal(); try { Thread.sleep(3000);//here, the thread is sleeping while another thread is not awaken since the lock is not releases System.out.println("------unlock--------"); Monitor.lock.unlock();//now the other thread is awaken, if I do not explicitly unlock , no thread will be awaken. } catch (InterruptedException e) { e.printStackTrace(); } } } public class Monitor { static ReentrantLock lock = new ReentrantLock(); static Condition condition = lock.newCondition(); static volatile boolean isConditionTrue = true; public static void main(String args[]) { Thread t1 = new WorkerThread(); Thread t2 = new WorkerThread(); t1.start(); t2.start(); Thread.sleep(2000); lock.lock(); isConditionTrue=true; condition.signalAll(); lock.unlock(); } }
OUTPUT:
THREAD ID 9-------working -------- ------singnall-------- ------unlock-------- THREAD ID 10-------working -------- ------singnall-------- ------unlock--------
你在Contition#await
错过了这句话:
在所有情况下,在此方法返回之前,当前线程必须重新获取与此条件关联的锁。 当线程返回时, 保证保持此锁定。
换句话说,您必须在await
之后显式释放锁定,就像signal
。
为什么这种机制是合理的:如果你第一次释放锁定,然后signal
,你就会对竞争状态开放,其他线程在释放锁定和信号到达停放线程之间做出了改变。 机制的工作方式,首先选择一个确定的线程被信号唤醒,然后它等待锁定,然后信号线程释放它,然后唤醒线程继续。
您可能会认为signal
可以在内部完成所有这些,但随后:
- API会变得混乱:释放锁的方法不止一个;
- APi将变得更具限制性并且排除线程想要在释放锁之前做更多事情的任何用例,例如primefaces地发出更多信号。