1. 概述
类似于 Object.wait() 和 Object.notify() 与ReentrantLock结合使用
2. 主要接口 (注意不是wait方法,有一个a开头 await)
void await() throws InterruptedException;
void awaitUninterruptibly();
long awaitNanos(long nanosTimeout) throws InterruptedException;
boolean await(long time, TimeUnit unit) throws InterruptedException;
boolean awaitUntil(Date deadline) throws InterruptedException;
void signal();
void signalAll();
3. API详解
await()方法会使当前线程等待,同时释放当前锁,当其他线程中使用signal()时或者signalAll()方法时,线程会重新获得锁并继续执行。
或者当线程被中断时,也能跳出等待。这和Object.wait()方法很相似。
awaitUninterruptibly()方法与await()方法基本相同,但是它并不会再等待过程中响应中断。
singal()方法用于唤醒一个在等待中的线程。相对的singalAll()方法会唤醒所有在等待中的线程。
这和Object.notify()方法很类似。
package com.john.learn.high.concurent.ch03;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class ReenterLockCondition implements Runnable {
public static ReentrantLock lock = new ReentrantLock();
public static Condition condition = lock.newCondition();
public void run() {
try {
lock.lock();
condition.await();
System.out.println("Thread [" + Thread.currentThread().getName() + "] is going on!");
Thread.sleep(1000);
lock.lock();
condition.signal();
lock.unlock();
} catch (InterruptedException e) {
} finally {
lock.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
ReenterLockCondition reenterLockCondition = new ReenterLockCondition();
for (int i = 0; i < 5; i++) {
Thread thread = new Thread(reenterLockCondition);
thread.start();
}
Thread.sleep(1000);
lock.lock();
condition.signal();
lock.unlock();
}
}
4. 程序实例 ArrayBlockingQueue
put 方法: 如果count == size Queue已经满了, 当前put线程必须等待 notfull 通知。
public void put(T item) {
final ReentrantLock lock = this.lock;
try {
lock.lockInterruptibly();
while (count == this.size) {
notFull.await();
}
this.insert(item);
} catch (InterruptedException e) {
notFull.signal();
} finally {
lock.unlock();
}
}
如果insert 成功,就必须通过notEmpty 不空 通知可以take 数据了。
private void insert(T item) {
items[this.putIndex] = item;
inc();
count++;
notEmpty.signal();
}
take 操作: 如果count == 0 ,当前线程必须等待新的数据。 等待notEmpty 的通知。
同时 extrace() 方法 由于take 数据,queue 不在full,通过notFull 唤醒其他线程,完成数据put
public T take() throws InterruptedException {
final ReentrantLock lock = this.lock;
try {
lock.lockInterruptibly();
while (this.count == 0) {
notEmpty.await();
}
return extrace();
} catch (InterruptedException e) {
notEmpty.signal();
throw e;
} finally {
lock.unlock();
}
}
具体实现:
package com.john.learn.high.concurent.ch03.tools;
import java.lang.reflect.Array;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class ArrayBlockingQueue<T> {
private final ReentrantLock lock = new ReentrantLock();
private final Condition notEmpty = lock.newCondition();
private final Condition notFull = lock.newCondition();
private Object[] items = null;
private int count;
private int putIndex;
private int takeIndex;
private int size;
public ArrayBlockingQueue(int size) {
this.items = new Object[size];
this.size = size;
}
public void put(T item) {
final ReentrantLock lock = this.lock;
try {
lock.lockInterruptibly();
while (count == this.size) {
notFull.await();
}
this.insert(item);
} catch (InterruptedException e) {
notFull.signal();
} finally {
lock.unlock();
}
}
public T take() throws InterruptedException {
final ReentrantLock lock = this.lock;
try {
lock.lockInterruptibly();
while (this.count == 0) {
notEmpty.await();
}
return extrace();
} catch (InterruptedException e) {
notEmpty.signal();
throw e;
} finally {
lock.unlock();
}
}
private T extrace() {
T item = (T) this.items[this.takeIndex];
takeIndex++;
if (takeIndex == this.size) {
takeIndex = 0;
}
count--;
this.notFull.signal();
return item;
}
private void insert(T item) {
items[this.putIndex] = item;
inc();
count++;
notEmpty.signal();
}
private void inc() {
putIndex++;
if (putIndex == this.size) {
putIndex = 0;
}
}
public static void main(String[] args) throws InterruptedException {
final ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<String>(5);
new Thread() {
public void run() {
for (int i = 1; i <= 100; i++) {
queue.put("Hi " + String.valueOf(i));
System.out.println("Put completed " + i);
}
};
}.start();
Thread reader = new Thread() {
public void run() {
while (true) {
if (Thread.interrupted()) {
break;
}
try {
System.out.println(queue.take());
} catch (InterruptedException e1) {
e1.printStackTrace();
Thread.currentThread().interrupt();
}
try {
Thread.sleep(10);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
};
};
reader.start();
Thread.sleep(5000);
reader.interrupt();
}
}
注意: 这里由于使用一个 ReentrantLock 完成 ArrayBlockQueue, put 和take 方法 还是线程堵塞的,即 put 和 take 无法在多个线程同时操作。后面的篇章将会讲解高性能的ArrayBlockQueue, put 和 take 操作互不干涉,像真正的MQ。