java多线程通信之等待唤醒机制

时间:2021-09-09 04:22:37

0概述

多线程间通信,其实就是多个线程操操作同一个资源,但是操作方式不同。典型实例有生产者和消费者,本文也通过实例来分析线程等待唤醒机制。

1相关API介绍

public final void notify()
唤醒在此对象监视器上等待的单个线程,如果所有线程都在此对象上等待,则会任意选择唤醒其中一个线程
public final void wait()
当前线程必须拥有此对象监视器。该线程发布对此监视器的所有权并等待,直到其他线程通过调用 notify 方法,或 notifyAll 方法通知在此对象的监视器上等待的线程醒来。然后该线程将等到重新获得对监视器的所有权后才能继续执行。
“为了实现监视器的排他性监视能力,JVM为每一个对象和类都关联一个锁;锁住了一个对象,就是获得对象相关联的监视器”。
wait(),notify(),notifyAll(),这些方法都使用在同步代码块中,因为要对持有监视器的线程进行操作,所以要使用在同步中,因为只有同步才有锁。值得说明的是:等待和唤醒必须是同一个锁(必须为同一个对象)由于锁可以是任意对象,所以可以被任意对象调用的方法定义在object类中

2 实例分析

仓库类

//等待和唤醒都是this锁
public class Storage<T> {
// 仓库最大存储量
private final int MAX_SIZE = 1000;
private int storageSize = 100;
private Object[] data;
private int top = 0;


public Storage(int size) {
size = size <= 0 ? storageSize : size;
size = size > this.MAX_SIZE ? this.MAX_SIZE : size;
this.storageSize = size;
data = new Object[this.storageSize];
}

public int getStorageSize() {
return storageSize;
}

public synchronized T pop() {
while (top == 0) {
try {
//等待
wait();
} catch (Exception ex) {
ex.printStackTrace();
}
}
top--;
T t = (T) data[top];
//唤醒所有线程
notifyAll();
return t;
}

public synchronized void push(T t) {
while (top == storageSize) {
try {
wait();
} catch (Exception ex) {
ex.printStackTrace();
}
}
data[top++] = t;
notifyAll();
}

}

生产者

public class Producer implements Runnable {
private Storage<String> storage;
private volatile boolean breakLoop = false;

public Producer(Storage<String> storage) {
this.storage = storage;
}

@Override
public void run() {
int index = 1;
while (true) {
if (breakLoop) {
break;
}
try {
Thread.sleep(1000);
} catch (Exception ex) {
ex.printStackTrace();
}
storage.push("data:" + index);
System.out.println("Producer data:" + index);
index++;
}
}

public void destroy() {
this.breakLoop = true;
}
}

消费者

public class Consumer implements Runnable {
private Storage<String> storage;
private volatile boolean breakLoop = false;

public Consumer(Storage<String> storage) {
this.storage = storage;
}

@Override
public void run() {
while (true) {
if (breakLoop) {
break;
}
try {
Thread.sleep(1000);
} catch (Exception ex) {
ex.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "Consumer:" + storage.pop());
}
}

public void destroy() {
this.breakLoop = true;
}
}

Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。此实现允许更灵活的结构,可以具有差别很大的属性,可以支持多个相关的 Condition 对象。

public class Storage<T> {
// 仓库最大存储量
private final int MAX_SIZE = 1000;
private int storageSize = 100;
private Object[] data;
private int top = 0;
private Lock lock = new ReentrantLock();
private Condition producerCon = lock.newCondition();
private Condition consumerCon = lock.newCondition();

public Storage(int size) {
size = size <= 0 ? storageSize : size;
size = size > this.MAX_SIZE ? this.MAX_SIZE : size;
this.storageSize = size;
data = new Object[this.storageSize];
}

public int getStorageSize() {
return storageSize;
}

public T pop() throws InterruptedException {
T t = null;
lock.lock();
try {

while (top == 0) {
//消费者等待
consumerCon.await();
}
top--;
t = (T) data[top];
//唤醒生产者
producerCon.signal();

} finally {
lock.unlock();
}
return t;
}

public void push(T t) throws InterruptedException {
lock.lock();
try {
while (top == storageSize) {
//生产者等待
producerCon.await();
}
data[top++] = t;
//唤醒消费者
consumerCon.signal();
} finally {
lock.unlock();
}
}

}