为什么等待条件释放锁定但信号没有?

时间:2014-11-21 10:09:51

标签: java multithreading concurrency locking monitor

我编写下面的代码来测试线程在等待Condition对象时何时处于唤醒状态。

  1. 但我发现在致电signal()后我必须解锁。此方法不会释放锁定,而await()将释放此锁定。
  2. 这是Condition#await

      

    与此条件关联的锁是原子释放,并且当前线程因线程调度而被禁用,并且在发生以下四种情况之一之前处于休眠状态:

    这是来自Conditon#signal

      

    唤醒一个等待线程。

         

    如果任何线程正在等待这种情况,则选择一个线程进行唤醒。然后该线程必须重新获取锁       等待回来。

    但在我的代码中,这不是真的,直到我们解锁。为什么这样设计?因为在我看来,当我们决定向其他人发出信号时,我们不应再持有锁,我错了吗?

    1. 因为我们可以在调用signalunlock之间做很多事情,比如说我睡了10秒,java究竟是什么时候发出了另一个线程的信号?还有另一个后台主题是我们signalunlock之间的工作吗?

        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();
          }
      
      }
      
    2. 输出:

      THREAD ID 9-------working --------
      ------singnall--------
      ------unlock--------
      THREAD ID 10-------working --------
      ------singnall--------
      ------unlock--------
      

1 个答案:

答案 0 :(得分:8)

您错过了Contition#await中的这句话:

  

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

换句话说,您必须在await之后明确释放锁定,就像使用signal一样。

为什么这个机制是合理的:如果你第一次释放锁,然后signal ed,你就会对竞争条件开放,其他线程在释放锁和信号到达停放线程之间做出了改变。机制的工作方式,首先选择一个确定的线程被信号唤醒,然后它等待锁定,然后信号线程释放它,然后唤醒线程继续。

你可能会认为signal可以在内部完成所有这些,但随后:

  1. API会变得混乱:释放锁的方法不止一个;
  2. APi会变得更具限制性,并且排除线程想要在释放锁之前做更多事情的任何用例,例如原子地发出更多信号。
相关问题