1. Java中线程协作的最常见的两种方式:利用Object.wait()、Object.notify()和使用Condition
(1)复习一下Object.wait()、Object.notify()
在java中,对于任意一个java对象,它都拥有一组定义在java.lang.Object上监视器方法,包括wait(),wait(long timeout),notify(),notifyAll(),这些方法配合synchronized关键字一起使用可以实现等待/通知模式。
以前的文章:https://blog.csdn.net/zhm1563550235/article/details/84259542
(2)什么是Condition接口?
Condition接口也提供了类似Object监视器的方法,通过与Lock配合来实现等待/通知模式。
2. Condition接口
接口Condition把Object的wait和notification方法(包括wait()、notify()、notifyAll())分解到不同的条件对象中。通过把这些条件和任意Lock实现的使用组合起来,起到让每个对象上具有多重等待集合的作用。这里Lock取代了同步方法、同步代码块,condition取代了Object的wait、notification方法。
3. Condition声明的方法
方法名称 | 描述 |
void await() | 在接收到信号或被中断以前,强制调用线程一直等待。 |
boolean await(long time, TimeUnit unit) | 在接收到信号、被中断,或者超过指定的等待时间之前,强制调用线程一直等待。 同样是在await()基础上增加了超时响应,不同的是:
|
long awaitNanos(long nanosTimeout) | 在接收到信号,被中断,或者超过指定的等待时间之前,强制调用线程一直等待。同样是在await()基础上增加了超时响应,不同的是: 返回值表示当前剩余的时间,如果在nanosTimeout之前被唤醒,返回值 = nanosTimeout - 实际消耗的时间,返回值 <= 0表示超时; |
void awaitUninterruptibly() | 在接收到信号之前,强制当前线程一直等待。 |
boolean awaitUntil(Date deadline) | 在接收到信号、被中断,或者超过指定的截止时间之前,强制当前的线程一直等待。 |
void signal() | 唤醒一个等待中的线程。 |
void signalAll() | 唤醒所有等待中的线程。 |
4. 获取Condition
Condition实例实质上被绑定到一个锁上。一个锁内部可以有多个Condition,即有多路等待和通知。要为特定 Lock 实例获得 Condition 实例,请使用其 newCondition() 方法。
Conditon newCondition() 返回用来与当前Lock实例一起使用的Condition实例。
类似于 object.wait()和object.notify()的功能。object.wait()与object.notify()需要结合synchronized使用。Condition需要结合ReentrantLock使用。
5. Condition与循环一起使用,防止"虚假唤醒"
在等待Condition时,允许发生"虚假唤醒",这通常作为对基础平台语义的让步。若使用"if(!条件)"则被"虚假唤醒"的线程可能继续执行。所以"while(!条件)"可以防止"虚假唤醒"。建议总是假定这些"虚假唤醒"可能发生,因此总是在一个循环中等待。
6. Condition的使用
(1)使用Object的wait()和notify()实现生产者消费者:
public class Test {
private int queueSize = 10;
private PriorityQueue<Integer> queue = new PriorityQueue<Integer>(queueSize);
public static void main(String[] args) {
Test test = new Test();
Producer producer = test.new Producer();
Consumer consumer = test.new Consumer();
producer.start();
consumer.start();
}
class Consumer extends Thread{
@Override
public void run() {
consume();
}
private void consume() {
while(true){
synchronized (queue) {
while(queue.size() == 0){
try {
System.out.println("队列空,等待数据");
queue.wait();
} catch (InterruptedException e) {
e.printStackTrace();
queue.notify();
}
}
queue.poll(); //每次移走队首元素
queue.notify();
System.out.println("从队列取走一个元素,队列剩余"+queue.size()+"个元素");
}
}
}
}
class Producer extends Thread{
@Override
public void run() {
produce();
}
private void produce() {
while(true){
synchronized (queue) {
while(queue.size() == queueSize){
try {
System.out.println("队列满,等待有空余空间");
queue.wait();
} catch (InterruptedException e) {
e.printStackTrace();
queue.notify();
}
}
queue.offer(1); //每次插入一个元素
queue.notify();
System.out.println("向队列取中插入一个元素,队列剩余空间:"+(queueSize-queue.size()));
}
}
}
}
}
(2)使用Condition实现消费者生产者
public class Test {
private int queueSize = 10;
private PriorityQueue<Integer> queue = new PriorityQueue<Integer>(queueSize);
private Lock lock = new ReentrantLock();
private Condition notFull = lock.newCondition();
private Condition notEmpty = lock.newCondition();
public static void main(String[] args) {
Test test = new Test();
Producer producer = test.new Producer();
Consumer consumer = test.new Consumer();
producer.start();
consumer.start();
}
class Consumer extends Thread{
@Override
public void run() {
consume();
}
private void consume() {
while(true){
lock.lock();
try {
while(queue.size() == 0){
try {
System.out.println("队列空,等待数据");
notEmpty.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
queue.poll(); //每次移走队首元素
notFull.signal();
System.out.println("从队列取走一个元素,队列剩余"+queue.size()+"个元素");
} finally{
lock.unlock();
}
}
}
}
class Producer extends Thread{
@Override
public void run() {
produce();
}
private void produce() {
while(true){
lock.lock();
try {
while(queue.size() == queueSize){
try {
System.out.println("队列满,等待有空余空间");
notFull.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
queue.offer(1); //每次插入一个元素
notEmpty.signal();
System.out.println("向队列取中插入一个元素,队列剩余空间:"+(queueSize-queue.size()));
} finally{
lock.unlock();
}
}
}
}
}