问题描述:桌上有一盘子,桌上有一个空盘,允许存放一只水果,爸爸可向盘内放苹果,妈妈可向盘内放桔子,儿子专等吃盘内的桔子,女儿专等吃盘中的苹果。
package Fruit;
import java.util.concurrent.locks.*;// java.util.concurrent 包含许多线程安全、测试良好、高性能的并发构建块
/*
问题描述:桌上有一盘子,桌上有一个空盘,允许存放一只水果,爸爸可向盘内放苹果,妈妈可向盘内放桔子,儿子专等吃盘内的桔子,女儿专等吃盘中的苹果。
关键技术:锁、
Object类是Java中所有类的父类,是Java底层级别的,Condition是语言级别的,具有更高的可控制性和扩展性。
为什么要使用?Condition?利用Object的方式实际上是指在对象Object对象监视器上只能拥有一个同步队列和一个等待队列; 并发包中的Lock拥有一个同步队列和多个等待队列。
Condition能够支持不响应中断,而通过使用Object方式不支持
Condition能够支持多个等待队列(new 多个Condition对象),而Object方式只能支持一个
Condition能够支持超时时间的设置,而Object不支持
Condition相比较而言,强大的地方在于它能够精确的控制多线程的休眠与唤醒(注意是唤醒,唤醒并不表示该线程一定能够得到资源锁)
Object的wait和notify,Condition使用await和signal
创建一个Condition对象是通过lock.newCondition()
void await() throws InterruptedException//当前线程进入等待状态,如果在等待状态中被中断会抛出被中断异常
当前线程调用condition.await()方法后,会使得当前线程释放lock然后加入到等待队列中
直至被signal/signalAll后会使得当前线程从等待队列中移至到同步队列中去, 直到获得了lock后才会从await方法返回,或者在等待时被中断会做中断处理。
void signal()//唤醒一个等待在condition上的线程,将该线程从等待队列中转移到同步队列中,如果在同步队列中能够竞争到Lock则可以从等待方法中返回。
如果不加上Thread.sleep()来让线程睡眠,结果就像是单线程一样,生产者填满队列,消费者清空队列。
这个线程在释放这个锁之后会加入这个锁的竞争中
修改sleep的睡眠时间,设置不同的休眠时间,可以观察到生产者与消费者也不会出现交替进行,还是随机的。
那么为什么要用Condition实现对确定线程的唤醒操作呢?唤醒了又不一定得到锁,这个需要使用到await()来让当前线程必须等到其他线程来唤醒才能控制生产者与消费者的交替执行。
Condition是个接口,基本的方法就是await()和signal()方法;
Condition依赖于Lock接口,生成一个Condition的基本代码是lock.newCondition()
调用Condition的await()和signal()方法,都必须在lock保护之内,就是说必须在lock.lock()和lock.unlock之间才可以使用
如果有多个消费者用Condition实现就非常简单了,如果使用Object监视器类也可以实现但是相对复杂,编程过程中容易出现死锁
*/
public class EatFruit {
public static void main(String[] args) {
Action r = new Action();
Father father = new Father(r);
Mother mother = new Mother(r);
Son son = new Son(r);
Daughter daughter = new Daughter(r);
//thread每个线程都独立,不共享资源 ,如果要共享需要加上同步条件synchronized
//在程序开发中只要是多线程肯定永远以实现Runnable接口为主。
//实现Runnable接口相比继承Thread类有如下好处:
//1、避免继承的局限,一个类可以继承多个接口。
//2、适合于资源的共享。
Thread f = new Thread(father);
Thread m = new Thread(mother)