Java实现线程间同步与互斥有两种方法:
- 关键字synchronized与wait()和notify()/notifyAll()方法相结合实现等待/通知模式
- ReentrantLock+Condition对象
水果盘问题是一个经典的多生产者消费者之间同步与互斥问题:
家中有一个水果盘,只能放一个水果,爸爸负责放橘子,妈妈放苹果,而儿子只吃苹果,女儿吃橘子。
下面就分别用以上两种方式解决该问题
synchronized与wait()和notify()/notifyAll()
先定义水果和盘子
enum Fruit{
APPLE, ORANGE, NONE
}
class Dish{
public static Fruit sFruit=Fruit.NONE;
}
生产者
class Product{
private Object lock;
private Fruit mFruit;//生产者放的水果
Product(Object lock){
this.lock=lock;
}
void setFruit(Fruit fruit){
mFruit=fruit;
}
void putFruit(){
synchronized (lock){
try {
String threadName=Thread.currentThread().getName();
//盘子里有水果则等待
while (!Dish.sFruit.equals(Fruit.NONE)){
System.out.println(threadName+": Dish is full and I am waiting...");
lock.wait();
}
System.out.println(threadName +": Dish is empty and I put an "+mFruit);
Dish.sFruit=mFruit;
//通知其他所有等待的线程
lock.notifyAll();
}catch (Exception e){
e.printStackTrace();
}
}
}
}
消费者
class Consumer{
private Object lock;
private Fruit mFruit;//消费者吃的水果
Consumer(Object lock){
this.lock=lock;
}
void setFruit(Fruit fruit){
mFruit=fruit;
}
void eatFruit(){
synchronized (lock){
try {
String threadName=Thread.currentThread().getName();
while (!Dish.sFruit.equals(mFruit)){
if (Dish.sFruit.equals(Fruit.NONE)) {
System.out.println(threadName +": Dish is empty and I am waiting");
}else {
System.out.println(threadName +": There is no fruit I eat and I am waiting");
}
lock.wait();
}
System.out.println(threadName +": I get an " +mFruit +" and then eat it.");
Dish.sFruit=Fruit.NONE;
lock.notifyAll();
}catch (Exception e){
e.printStackTrace();
}
}
}
生产者线程
class ProductThread extends Thread{
private Product mProduct;
ProductThread(Product product, String name, Fruit fruit){
mProduct=product;
mProduct.setFruit(fruit);
setName(name);
}
@Override
public void run() {
while (true){
mProduct.putFruit();
}
}
}
消费者线程
class ConsumerThread extends Thread{
private Consumer mConsumer;
ConsumerThread(Consumer consumer, String name, Fruit fruit){
mConsumer=consumer;
mConsumer.setFruit(fruit);
setName(name);
}
@Override
public void run() {
while (true){
mConsumer.eatFruit();
}
}
}
main
public class FruitDish {
public static void main(String args[]) {
Object lock=new Object();
ProductThread father=new ProductThread(new Product(lock), "Father", Fruit.ORANGE);
ProductThread mather=new ProductThread(new Product(lock), "Mather", Fruit.APPLE);
ConsumerThread son=new ConsumerThread(new Consumer(lock), "Son", Fruit.APPLE);
ConsumerThread daughter=new ConsumerThread(new Consumer(lock), "Daughter", Fruit.ORANGE);
father.start();
mather.start();
son.start();
daughter.start();
}
}
打印结果
Father: Dish is empty and I put an ORANGE
Father: Dish is full and I am waiting...
Son: There is no fruit I eat and I am waiting
Mather: Dish is full and I am waiting...
Daughter: I get an ORANGE and then eat it.
Daughter: Dish is empty and I am waiting
Mather: Dish is empty and I put an APPLE
Mather: Dish is full and I am waiting...
Son: I get an APPLE and then eat it.
Son: Dish is empty and I am waiting
Father: Dish is empty and I put an ORANGE
Father: Dish is full and I am waiting...
可以看到实现了生产者消费者之间的同步与互斥,但 lock.notifyAll() 会唤醒所有等待的线程,例如爸爸放了一个橘子后,有可能是妈妈,儿子,女儿都被唤醒,这样就白白浪费了时间,能不能爸爸只通知唤醒女儿呢。用wait()和notify()/notifyAll()无法实现,但可以用Condition来做
Condition
关键字synchronized与wait()和notify()/notifyAll()方法相结合可以实现等待/通知模式,类似ReentrantLock也可以实现同样的功能,但需要借助于Condition对象。
特殊之处:synchronized相当于整个ReentrantLock对象只有一个单一的Condition对象情况。而一个ReentrantLock却可以拥有多个Condition对象,来实现通知部分线程。
生产者
class Product{
private ReentrantLock lock;
private Condition mCondition;
private Fruit mFruit;//生产者放的水果
Product(ReentrantLock lock, Condition condition){
this.lock=lock;
mCondition=condition;
}
void setFruit(Fruit fruit){
mFruit=fruit;
}
void putFruit(){
lock.lock();
try {
String threadName=Thread.currentThread().getName();
//盘子里有水果则等待
while (!Dish.sFruit.equals(Fruit.NONE)){
System.out.println(threadName+": Dish is full and I am waiting...");
mCondition.await();
}
System.out.println(threadName +": Dish is empty and I put an "+mFruit);
Dish.sFruit=mFruit;
//通知其他所有等待的线程
mCondition.signalAll();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
消费者
class Consumer{
private ReentrantLock lock;
private Condition mCondition;
private Condition mOtherCondition;
private Fruit mFruit;//消费者吃的水果
Consumer(ReentrantLock lock,Condition condition, Condition otherCondition){
this.lock=lock;
mCondition=condition;
mOtherCondition=otherCondition;
}
void setFruit(Fruit fruit){
mFruit=fruit;
}
void eatFruit(){
lock.lock();
try {
String threadName=Thread.currentThread().getName();
while (!Dish.sFruit.equals(mFruit)){
if (Dish.sFruit.equals(Fruit.NONE)) {
System.out.println(threadName +": Dish is empty and I am waiting");
}else {
System.out.println(threadName +": There is no fruit I eat and I am waiting");
}
mCondition.await();
}
System.out.println(threadName +": I get an " +mFruit +" and then eat it.");
Dish.sFruit=Fruit.NONE;
mOtherCondition.signalAll();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
生产者消费者线程未变
main
public class FruitDish {
public static void main(String args[]) {
ReentrantLock lock=new ReentrantLock();
Condition[] conditions=new Condition[2];
conditions[0]=lock.newCondition();
conditions[1]=lock.newCondition();
ProductThread father=new ProductThread(new Product(lock, conditions[0]), "Father", Fruit.ORANGE);
ProductThread mather=new ProductThread(new Product(lock, conditions[1]), "Mather", Fruit.APPLE);
ConsumerThread son=new ConsumerThread(new Consumer(lock, conditions[1], conditions[0]), "Son", Fruit.APPLE);
ConsumerThread daughter=new ConsumerThread(new Consumer(lock, conditions[0], conditions[1]), "Daughter", Fruit.ORANGE);
father.start();
mather.start();
son.start();
daughter.start();
}
}
打印结果
Mather: Dish is empty and I put an APPLE
Mather: Dish is full and I am waiting...
Son: I get an APPLE and then eat it.
Son: Dish is empty and I am waiting
Father: Dish is empty and I put an ORANGE
Father: Dish is full and I am waiting...
Daughter: I get an ORANGE and then eat it.
Daughter: Dish is empty and I am waiting
Mather: Dish is empty and I put an APPLE
Mather: Dish is full and I am waiting...
Son: I get an APPLE and then eat it.
Son: Dish is empty and I am waiting
可以看到打印结果基本很规律,因为父亲女儿共用一个condition,母亲儿子共用一个,父亲放了橘子,只有女儿被通知到,女儿被唤醒吃完了后通知母亲,这样既保证效率高同时也保证了公平(父母间隔放水果)