线程通信之生产者和消费者案例
使用 Object 类的 wait() 和 notify() 方法 (不适用锁机制)
只有同步监听对象才可以调用 wait() 和 notify() 方法,否则报错
线程之间进行通信,且防止耦合度过高,使用一个中间类作为通信的共同资源
- 需要使用 synchronized 保证一个过程的原子性
- 使用 isEmpty 变量作为标志参数,在结束生产和结束消费之后改变该值
- 使用 Object 类的 wait() 方法,判断资源状态,若存在则当前进程进入等待池
- 使用 Object 类的 notify() 方法,在结束生产和结束消费之后唤醒其他线程
- 值得注意的是:只有同步监听对象才可以调用 wait() 和 notify() 方法,否则报错
测试类
source 作为两个线程的参数传入
public class TestDemo {
public static void main(String[] args) {
ShareSources source = new ShareSources();
new Producer(source).start();
new Comsumer(source).start();
}
}
资源类
public class ShareSources {
private String name;
private String sex;
private Boolean isEmpty = true;//使用一个参数作为标志
/**
* 存入数据
* @param name
* @param sex
*/
synchronized public void push(String name, String sex) {//同步方法,保证了该方法的原子性
try {
//此处使用While比If更加合理,
while (!isEmpty) {// 生产者是当前线程,如果资源任存在,则当前线程释放锁,进入等待池中,释放后,消费者进程获得锁
this.wait();//this 指的是 source 对象 进入等待池中的线程只能被其他线程唤醒,这里只能在消费者线程中唤醒
}
//---------------------开始生产-------------------------
this.name = name;
Thread.sleep(10);
this.sex = sex;
//---------------------结束生产-------------------------
isEmpty = false;
this.notifyAll();//同时将其他线程唤醒
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取资源
*/
synchronized public void get() {
try {
while (isEmpty) {//如果资源不存在
this.wait();//当前线程是消费者,进入等待池
}
} catch (InterruptedException e) {
e.printStackTrace();
}
//---------------------开始消费-------------------------
Thread.sleep(10);
System.out.println(this.name + "-" + this.sex);
//---------------------结束消费-------------------------
isEmpty = true;
this.notifyAll();//同时将其他线程唤醒
}
}
生产者类
生产者可能存在多个,且需要与消费者不同线程
public class Producer extends Thread {
private ShareSources source = null;
public Producer(ShareSources source) {//通过构造器获取相同的资源
this.source = source;
}
@Override
public void run() {
for (int i = 0; i < 50; i++) {
if (i % 2 == 0) {
source.push("春哥", "男");
}else {
source.push("凤姐", "女");
}
}
}
}
消费者类
public class Comsumer extends Thread {
private ShareSources source = null;
public Comsumer(ShareSources source) {
this.source = source;
}
public void run() {
for (int i = 0; i < 50; i++) {
source.get();
}
}
}
使用 Lock 和 condition 接口
资源类(只有该类发生变化)
需要注意的是,condition 的操作一定要在获取锁之后,释放锁之前执行,否则报错
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ShareSources {
private String name;
private String sex;
private Boolean isEmpty = true;
// 实例化锁
private final Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();// 由于Condition是一个接口,lock.newCondition()方法返回一个绑定的Condition实例
/**
* 存入数据
*
* @param name
* @param sex
*/
public void push(String name, String sex) {
lock.lock();
try {
if (!isEmpty) {
condition.await();// 使用condition的await()方法相当于 Object 类的 wait 方法
}
this.name = name;
Thread.sleep(10);
this.sex = sex;
isEmpty = false;
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
/**
* 获取资源
*/
public void get() {
lock.lock();
try {
if (isEmpty) {
condition.await();// 使用condition的await()方法相当于 Object 类的 wait 方法
}
Thread.sleep(10);
System.out.println(this.name + "-" + this.sex);
isEmpty = true;
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
岑忠满的博客新站点
http://cenzm.xyz