代码之家  ›  专栏  ›  技术社区  ›  javaworld

使用rentrantlock实现生产者消费者时发生非法监视器状态异常

  •  0
  • javaworld  · 技术社区  · 6 年前

    在Java中尝试使用ReNANTTROLK实现生产者消费者

        Condition producerlock = lock.newCondition();
        Condition consumerlock = lock.newCondition();
    

    它有两个条件,一个是生产者,另一个是消费者。

    这里我们有一个带有两个方法producer consumer和一个堆栈的processor类

       Stack<Integer> hellostrack = new Stack<>();
    
    
    
     public void produce() throws InterruptedException {
            lock.tryLock();
            System.out.println("inside producer method");
            while (true) {
                try {
    
                    if (hellostrack.size() > 8) {
                        System.out.println("stack is full its time for me to go to sleep");
                        producerlock.await();
                    }
                    System.out.println("thread is alive and kicking");
                    hellostrack.add(new Random().nextInt());
                    consumerlock.signalAll();
    
    
                } finally {
                    System.out.println("Exception occours in producer Thread");
                    lock.unlock();
                }
            }
        }
    
    
    public void consume() throws InterruptedException{
                 System.out.println("inside consumer method");
                 lock.tryLock();
              try {
                  while (true) {
                      if (hellostrack.isEmpty()) {
                          System.out.println("stack is empty im going to sleep");
                          consumerlock.await();
    
                      } else {
                          System.out.println("poping elelmts from stock" + hellostrack.pop());
                          consumerlock.signalAll();
    
    
                      }
    
              } }finally {
                  System.out.println("Exception occours at consumer");
                  lock.unlock();
              }
         }
    

    正如您所看到的,当堆栈达到某个极限时,生产者将进入休眠状态,当堆栈为空时,生产者也会进入休眠状态。

    但当我用两条线运行它们时

    Processor p  = new Processor();
            Thread t1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        p.consume();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
    
            Thread t12 = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        p.produce();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
    
            t1.start();
            t12.start();
    
    
    
    i get illegal state exception 
    
    
    inside consumer method
    stack is empty im going to sleep
    inside producer method
    thread is alive and kicking
    Exception occours in producer Thread
    thread is alive and kicking
    Exception occours in producer Thread
    Exception in thread "Thread-1" java.lang.IllegalMonitorStateException
        at java.base/java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:149)
        at java.base/java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1302)
        at java.base/java.util.concurrent.locks.ReentrantLock.unlock(ReentrantLock.java:439)
        at Processor.produce(Processor.java:30)
        at Processor$2.run(Processor.java:76)
        at java.base/java.lang.Thread.run(Thread.java:834)
    poping elelmts from stock891164354
    poping elelmts from stock-1958956829
    stack is empty im going to sleep
    
    2 回复  |  直到 6 年前
        1
  •  1
  •   Slaw    6 年前

    除了 @JohnVint's answer ,您的代码还有一些其他问题。

    1. 你在用 Lock.tryLock() :

      只有在调用时锁是空闲的情况下才获取锁。

      获取锁(如果可用)并立即返回值true。如果锁不可用,则此方法将立即返回值false。

      这种方法的典型用法是:

      Lock lock = ...;
      if (lock.tryLock()) {
        try {
           // manipulate protected state
        } finally {
          lock.unlock();
        }
      } else {
        // perform alternative actions
      }
      

      此用法确保了锁被获取时解锁,并且如果未获取锁,则不会尝试解锁。

      你的代码没有检查 tryLock 这意味着线程有机会在不持有锁的情况下进入保护代码。这意味着有机会打电话 await() , signalAll() unlock() 除了不正确的同步访问之外,不持有锁。

      在这种情况下要调用的方法是 Lock.lock() :

      获取锁。

      如果锁不可用,则当前线程将被禁用以进行线程调度,并处于休眠状态,直到获取锁为止。

      这意味着线程将等到获得锁后再继续。但是,因为你的方法已经 InterruptedException 你还是用 Lock.lockInterruptibly() . 基本上和 lock() 但是等待会被打断。

    2. 你在打电话 consumerlock.signalAll() 在你的内心 consume() 方法。

      一旦使用了一个元素,就要通知 生产者 还有更多的空间。你应该打电话来 producerlock.signalAll() .

    3. 你不打电话 等待() 在一个圈内。

      打电话来是个好习惯 等待() 在检查条件的循环中。原因是一个线程可以唤醒任何原因(很少)。如果发生这种情况,并且有一个循环,线程将重新检查条件,如果合适的话,将调用 等待() 再一次。

      另外,你正在使用 信号() . 该方法通知 全部的 等待线程唤醒,尝试获取锁,然后继续。由于不使用循环,所有唤醒的线程都将继续执行任何可能导致不一致/不正确状态的修改。相反,有循环意味着如果唤醒的线程之一再次导致等待条件为true,则任何后续线程都将返回等待状态。

      使用循环看起来像:

      while (hellostrack.size() > 8) { // should this be >= 8?
          producerlock.await();
      }
      
      // and
      
      while (hellostrack.isEmpty()) {
          consumerlock.await();
      }
      
        2
  •  1
  •   John Vint    6 年前

    注意你在 while(true) 循环。你永远不会离开环,这意味着你解锁,然后再也不会重新锁定。在第一个循环之后,您将再次尝试解锁,但由于您不拥有锁,它将引发异常。

    移动 tryLock 虽然(真) 可能会更好。