常用的解决方案中,卖完饭了,会采用Object类的notiryAll的方式唤醒所有线程,这样做其实是浪费和低效的,因为唤醒所有线程的时候,会把卖饭的线程也唤醒。现在换用Lock锁的方式来解决,一个Lock可以new多个Condition出来,通过condition来唤醒对应condition上等待的线程。
卖饭类:
package com.sniper.thread.lock.domain;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* 餐馆,负责卖饭
* @author audaque
*
*/
public class Restaurant implements Runnable {
public void sale() {
while(true) {
MyLock.MY_LOCK.lock();
List<Food> foods = Window.foods;
if(foods.size() > 0) {
Food food = foods.get(0);
System.out.println(Thread.currentThread().getName() + "卖了第" + food.getId() + "号食物。。。");
/*try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
//卖出一份,减少一份
foods.remove(0);
//MyLock.CON.signal();//随机唤醒等待中的一条线程
MyLock.COOK_CON.signalAll();;//唤醒等待中全部线程
} else {
System.out.println(Thread.currentThread().getName() + ":食物卖完了,我休息了。。。");
try {
MyLock.R_CON.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
MyLock.MY_LOCK.unlock();
}
}
@Override
public void run() {
sale();
}
}
做饭类:
package com.sniper.thread.lock.domain;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* 厨师,负责做饭
* @author audaque
*
*/
public class Cook implements Runnable {
public static int num = 1;
public void make() {
while(true) {
MyLock.MY_LOCK.lock();
List<Food> foods = Window.foods;
if(foods.size() <= Window.max_num) {
Food food = new Food(num);
foods.add(food);
System.out.println(Thread.currentThread().getName() + "做了第" + food.getId() + "号食物。。。");
/*try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
num++;
/*
* notify随机唤醒一条线程,容易产生问题
* 假设三个做饭的,三个卖饭的
* 在某一瞬间三个卖饭的都处于等待,两个做饭的也处于等待
* 最后一个做饭的做好一份饭之后,随机唤醒其中一条,假如唤醒的是做饭线程,
* 此时,两条醒着的做饭线程判断foods.size() > Window.max_num(1)
* 然后两条做饭线程也休息了,此时,六条线程都会处于等待状态,没人会唤醒他们
* 也就是死锁
*/
//MyLock.CON.signal();//随机唤醒等待中的一条线程
MyLock.R_CON.signalAll();;//唤醒等待中全部线程
} else {
System.out.println(Thread.currentThread().getName() + ":窗口放满食物了,我休息了。。。");
try {
MyLock.COOK_CON.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
MyLock.MY_LOCK.unlock();
}
}
@Override
public void run() {
make();
}
}
窗口,从窗口取饭卖,做完饭往窗口放:
package com.sniper.thread.lock.domain;
import java.util.ArrayList;
import java.util.List;
/**
* 窗口,厨师做完饭往窗口放,餐馆从窗口拿饭卖
* @author sniper
*
*/
public class Window {
public static List<Food> foods = new ArrayList<Food>();
//窗口最大容纳食物数量
public static int max_num = 1;
}
食物类:
package com.sniper.thread.lock.domain;
/**
* 食物
* @author sniper
*
*/
public class Food {
private int id;
private String name;
public Food(int id) {
super();
this.id = id;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
App:
package com.sniper.thread.lock.domain;
public class App {
public static void main(String[] args) {
Cook cook = new Cook();
//三条做饭线程
for(int i=0; i<3; i++) {
new Thread(cook).start();
}
Restaurant r = new Restaurant();
//十条卖饭线程
for(int i=0; i<3; i++) {
new Thread(r).start();
}
}
}
锁类:
package com.sniper.thread.lock.domain;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class MyLock {
public static final Lock MY_LOCK = new ReentrantLock();
public static final Condition R_CON = MY_LOCK.newCondition();
public static final Condition COOK_CON = MY_LOCK.newCondition();
private MyLock() {}
}