线程通信之生产者和消费者案例

使用 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();
}
}
}

posted @ 2017-08-08 16:58  岑忠满  阅读(238)  评论(0)    收藏  举报