2014-10-19 83 views
1

我想让我的生产者/消费者工作,我故意不想使用BlockingQueue在这里了解更细的细节。我明白,当我调用object.wait()时,线程松开它的锁并进入WAITING状态,直到有人通知(notify/notifyAll)使其返回到BLOCKED状态,并且如果线程获取锁,则该线程将转到RUNNABLE。可以等待的线程,再次唤醒上下文切换

private class Consumer implements Runnable{ 
     private final MyQueue<Integer> queue; 
     public Consumer(MyQueue<Integer> queue){ 
      this.queue = queue; 
     } 
     @Override 
     public void run(){ 
      while(true){ 
       synchronized (queue) { 
        //Block till new available 
        while(queue.isEmpty()){ 
         try { 
          queue.wait(); 
         } catch (InterruptedException e) { 
          e.printStackTrace(); 
         } 
        } 
        //Consume 
        queue.dequeue(); 
        queue.notifyAll(); //Notify people waiting on queue full condition 
       } 
      } 
     } 
    } 

我的问题是有可能,(上面相对于代码):

  1. 我的线程在等待和观望后醒来(有些人通知) 和我得到了锁
  2. 我做了一个检查queue.isEmpty(),它不是空的,执行到下一行
  3. 就在下一行queue.dequeue()被执行之前,CPU上下文切换了我的线程。
  4. 下一页当我得到我的CPU片和锁,我运行queue.dequeue(),并说队列为空

我知道通常CPU调度程序提供了时间量子给每个线程,以避免上下文切换成本。

回答

1

这种情况不会发生,正是因为对队列的任何修改(或至少应该)都是从队列上的同步块完成的。因此,在while循环结束和dequeue()调用之间没有其他线程可以修改队列,因为您的线程是持有锁的那个线程。

当然,如果其他使用者也从队列中删除元素而未在队列上同步,则会出现此问题。但这只会是你的代码中的一个错误。使用封装是避免这些类型错误的最佳方法。如果您使用BlockingQueue,则BlockingQueue类将封装对共享状态的独占访问权限,并且您不必确保对队列的每个访问都已正确同步,因为BlockingQueue会为您执行此操作。

+0

上下文切换的线程是否保存锁定并退出执行? – Monish 2014-10-19 13:45:58

+0

线程保持锁定状态直到它离开同步块。线程当前是否正在执行并不会改变任何内容。如果调度程序决定在while循环之后和调用dequeue之前立即切换到其他线程,则等待该锁的所有其他线程将继续被阻塞,直到持有锁的线程重新计划为止,并继续执行直到它离开同步块。 – 2014-10-19 13:50:18

+0

谢谢!这使得它非常清晰:“线程保持锁定状态,直到它离开同步块为止,线程当前是否正在执行不会改变任何内容”。虽然有趣的是,当OS上下文切换时,它可能会减慢正在等待锁定的线程 – Monish 2014-10-19 13:59:44