整理了一个简单的生产者与消费者demo,中间意外的各种出现 IllegalMonitorStateException 异常,看了好半天才发现问题不在于逻辑,而是由于一个错误的方法使用。
在ReentrantLock 中,lock()方法是一个无条件的锁,与synchronize意思差不多,但是另一个方法 tryLock()方法只有在成功获取了锁的情况下才会返回true,如果别的线程当前正持有锁,则会立即返回false!如果为这个方法加上timeout参数,则会在等待timeout的时间才会返回false或者在获取到锁的时候返回true。
在demo中,获取锁的时候使用的是tryLock(),从而导致没有真正获取到锁,持有锁的仍然是另一个线程,于是调用signalAll()就会出现IllegalMonitorStateException ,由于这边锁出现问题,导致生产者消费者模式完全无法正常工作。
public class CacheQueue<T> {
ReentrantLock lock = new ReentrantLock();
Condition pCondition = lock.newCondition();
Condition cCondition = lock.newCondition();
int MAX;
List<T> cache;
public CacheQueue(int max) {
MAX = max;
cache = new ArrayList<T>(MAX);
}
public void produce(T t) {
/**
* tryLock()会导致获取不到锁
*/
lock.lock();
// lock.tryLock();
if (lock.isLocked()) {
try {
if (cache.size() == MAX) {
System.out.println("Tid: " + Thread.currentThread().getId() + " produce wait." + " cache size:" + cache.size());
pCondition.await();
} else {
cache.add(t);
System.out.println("Tid: " + Thread.currentThread().getId() + " produce:" + t.toString() + " cache size:" + cache.size());
}
//放到这里唤醒似乎更好一些
cCondition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
} else {
System.out.println("tryLock failed.");
}
}
public void consume() {
lock.lock();
// lock.tryLock();
if (lock.isLocked()) {
try {
if (cache.size() == 0) {
System.out.println("Tid: " + Thread.currentThread().getId() + " consume wait." + " cache size:" + cache.size());
cCondition.await();
} else {
T t = cache.remove(cache.size() - 1);
System.out.println("Tid: " + Thread.currentThread().getId() + " consume:" + t.toString() + " cache size:" + cache.size());
}
//放到这里唤醒似乎更好一些
pCondition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
} else {
System.out.println("tryLock failed.");
}
}
}
测试:
public class PCTest {
public static void main(String args[]){
PCTest test=new PCTest();
test.start();
}
public void start() {
System.out.println("PCTest start.");
CacheQueue<Integer> queue = new CacheQueue<>(30);
for (int i = 0; i <1;i++) {
new Thread(new ConsumerRunnable(queue)).start();
}
for(int i=0;i<5;i++){
new Thread(new ProducerRunnable(queue)).start();
}
}
class ConsumerRunnable implements Runnable {
CacheQueue queue;
public ConsumerRunnable(CacheQueue queue) {
this.queue = queue;
}
@Override
public void run() {
while (true) {
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
queue.consume();
}
}
}
class ProducerRunnable implements Runnable {
CacheQueue queue;
public ProducerRunnable(CacheQueue queue) {
this.queue = queue;
}
@Override
public void run() {
while (true) {
try {
Thread.sleep(500);
} catch (Exception e) {
e.printStackTrace();
}
queue.produce(new Random().nextInt());
}
}
}
}