线程通信:当任务之间有关系的时候,就需要多个线程通信,互相协调一起完成工作,当然是在线程同步的基础上通信协调。
1,一般用Object的wait/notify/notifyAll,让线程等待和唤醒实现线程之间的通信,而且上述方法只能由同步监听对象(共享资源)来调用,否则会报IllegalMonitorStateException异常。
2,还可以通过ReentranLock,使用condition的await/signal来唤醒和使线程等待。
生产者和消费者案例
下面以厨师做菜和服务员上菜,给取餐台(多线程操作的资源)放菜,上菜为例,用代码实现。
1,取餐台,多线程操作的资源,资源操作的同步使用Synchronized方法和ReentrantLock两种方式实现。
/**
* 取餐台,多线程操作的资源
*/
public class FoodTable {
private volatile int num; // 取餐台菜的数量
private Lock lock = new ReentrantLock(); // 可重入锁
private Condition condition = lock.newCondition();
/**
* 做菜,用synchronized修饰,同一时刻只能有一个线程操作该资源对象
* @return
*/
public synchronized void cook() {
try {
while (num != 0) { // 取餐台菜品不为零,厨师不做菜
this.wait(); // 厨师线程等待
}
num++; // 厨师做菜,取餐台菜品加1
System.out.println("厨师正在做菜,做菜好忙,累死。。。");
this.notify(); // 做好菜唤醒服务员线程上菜
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 上菜,用synchronized修饰,同一时刻只能有一个线程操作该资源对象
* @return
*/
public synchronized void deliver() {
try {
while (num == 0) { // 取餐台菜品为零,服务员不用上菜
this.wait(); // 服务员线程等待
}
num--; // 服务员做菜,取餐台菜品减1
System.out.println("服务员正在上菜,简简单单上个菜,好爽。。。");
this.notify(); // 上完菜唤醒厨师线程上菜
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 做菜,使用ReentrantLock显式保证同步,同一时刻只能有一个线程操作该资源对象
* @return
*/
public synchronized void cookLock() {
try {
lock.lock();
while (num != 0) { // 取餐台菜品不为零,厨师不做菜
condition.await(); // 厨师线程等待
}
num++; // 厨师做菜,取餐台菜品加1
System.out.println("厨师正在做菜,做菜好忙,累死。。。");
condition.signal(); // 做好菜唤醒服务员线程上菜
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
/**
* 上菜,使用ReentrantLock显式保证同步,同一时刻只能有一个线程操作该资源对象
* @return
*/
public void deliverLock() {
try {
lock.lock();
while (num == 0) { // 取餐台菜品为零,服务员不用上菜
condition.await(); // 服务员线程等待
}
num--; // 服务员做菜,取餐台菜品减1
System.out.println("服务员正在上菜,简简单单上个菜,好爽。。。");
condition.signal(); // 上完菜唤醒厨师线程上菜
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
2,厨师线程
/**
* 厨师,生产者,做菜
*/
public class Chef implements Runnable {
private FoodTable foodTable;
public Chef(FoodTable foodTable) { // 保证线程操作的是同一个资源,通过构造器传入同一资源对象
this.foodTable = foodTable;
}
@Override
public void run() {
for (int i = 1; i <= 10; i++) { // 为了测试,只执行10次
foodTable.cook(); // 做菜
}
}
}
3,服务员线程
/**
* 服务员,消费者,上菜
*/
public class Waiter implements Runnable {
private FoodTable foodTable;
public Waiter(FoodTable foodTable) { // 保证线程操作的是同一个资源,通过构造器传入同一资源对象
this.foodTable = foodTable;
}
@Override
public void run() {
for (int i = 1; i <= 10; i++) { // 为了测试,只执行10次
foodTable.deliver(); // 上菜
}
}
}
4,测试还是用餐馆来吧QAQ
/**
* 餐馆,厨师和服务员协调工作
* 假设只有一个厨师和一个服务员,厨师做好菜放到取餐台,厨师等待并唤醒服务员上菜,
* 服务员上菜,此时取餐台菜品为零,等待并唤醒厨师做菜,以此往复协调工作
*/
public class Restaurant {
public static void main(String[] args) {
// 实例化厨师,服务员,取餐台(资源)
FoodTable foodTable = new FoodTable();
Thread provider = new Thread(new Chef(foodTable)); // 创建厨师线程
Thread consumer = new Thread(new Waiter(foodTable)); // 创建服务员线程
provider.start(); // 厨师线程启动
consumer.start(); // 服务员线程启动
}
}
5,简单看下输出
厨师正在做菜,做菜好忙,累死。。。
服务员正在上菜,简简单单上个菜,好爽。。。
厨师正在做菜,做菜好忙,累死。。。
服务员正在上菜,简简单单上个菜,好爽。。。
厨师正在做菜,做菜好忙,累死。。。
服务员正在上菜,简简单单上个菜,好爽。。。
厨师正在做菜,做菜好忙,累死。。。
服务员正在上菜,简简单单上个菜,好爽。。。
厨师正在做菜,做菜好忙,累死。。。
服务员正在上菜,简简单单上个菜,好爽。。。
厨师正在做菜,做菜好忙,累死。。。
服务员正在上菜,简简单单上个菜,好爽。。。
厨师正在做菜,做菜好忙,累死。。。
服务员正在上菜,简简单单上个菜,好爽。。。
厨师正在做菜,做菜好忙,累死。。。
服务员正在上菜,简简单单上个菜,好爽。。。
厨师正在做菜,做菜好忙,累死。。。
服务员正在上菜,简简单单上个菜,好爽。。。
厨师正在做菜,做菜好忙,累死。。。
服务员正在上菜,简简单单上个菜,好爽。。。