使用RentrantLock实现生产者使用者时发生IllegalMonitorStateException

时间:2019-02-27 02:22:09

标签: java multithreading

im试图使用Java中的ReentrantLock实现生产者使用者

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

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

在这里,我们有一个处理器类,其中有两种方法生产者使用者和一个堆栈

   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 个答案:

答案 0 :(得分:1)

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

tryLock移到while(true)下,可能会更好。

答案 1 :(得分:1)

除了@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. 您不会在循环内调用await()

    优良作法是在检查条件的循环内调用await()。原因是因为线程可以出于任何原因(很少)唤醒。如果发生这种情况,并且存在一个循环,则线程将重新检查条件,并在适当的情况下再次调用await()

    此外,您正在使用signalAll()。该方法通知所有等待线程唤醒,尝试获取锁,然后继续。由于您不使用循环,所有唤醒的线程将继续执行任何修改-可能导致状态不一致/错误。相反,具有循环意味着如果唤醒的线程之一导致等待条件再次成立,则任何后续线程将回到等待状态。

    使用循环看起来像:

    while (hellostrack.size() > 8) { // should this be >= 8?
        producerlock.await();
    }
    
    // and
    
    while (hellostrack.isEmpty()) {
        consumerlock.await();
    }