生产者消费者问题描述是有一块缓冲区做为仓库,生产者能够将产品放入仓库,消费者则能够从其取走产品。java
1. 同步:效率高易实现,代码控制性较好,经常使用;安全
2. 管道:不易控制,被传输数据对象不易于封装,实用性不强。(PipedInputStream/PipedOutputStream)并发
(1)核心:保证同一资源被多个线程并发访问时的完整性。函数
(2)方法:测试
1. wait()/notify()this
2. await()/signal()spa
3. BlockingQueue阻塞队列方法.net
是基类Object的两个方法,意味着全部Java类均可拥有,故可为任何对象实现同步机制。线程
wait():当缓冲区已满/空时,对应线程中止本身的执行,放弃锁,等待;设计
notify():线程执行后,向其余等待的线程发出可执行的通知,同时放弃锁,等待。
【代码】仓库使用时生产者与消费者同步synchronized(仓库),同步代码内根据条件(同步对象.wait¬ify),不直接在生产者类Producer和消费者类Consumer中实现这两个方法,用Storage类中的实现:
可彻底取代wait() / nofity(),但同时锁定机制Lock直接
挂钩,具备更大的灵活性。在Lock对象调用newCondition()方法,将条件变量和一个锁对象进行绑定,进而控制并发程序访问竞争资源的安全。
只须要更新仓库类Storage的代码便可,生产者Producer、消费者Consumer、测试类Test的代码均不须要进行任何更改。这样咱们就知道为神马我要在Storage类中定义public void produce(int num);和public void consume(int num);方法,并在生产者类Producer和消费者类Consumer中调用Storage类中的实现了吧。将可能发生的变化集中到一个类中,不影响原有的构架设计,同时无需修改其余业务层代码。
BlockingQueue是JDK5.0的新增内容,它是一个已经在内部实现了同步的队列,实现方式采用的是咱们第2种await() / signal()方法。它能够在生成对象时指定容量大小。它用于阻塞操做的是put()和take()方法。
put()方法:相似于咱们上面的生产者线程,容量达到最大时,自动阻塞。
take()方法:相似于咱们上面的消费者线程,容量为0时,自动阻塞。
固然,你会发现这时对于public void produce(int num);和public void consume(int num);方法业务逻辑上的实现跟前面两个例子不太同样,不要紧,这个例子只是为了说明BlockingQueue阻塞队列的使用。
有时使用BlockingQueue可能会出现put()和System.out.println()输出不匹配的状况,这是因为它们之间没有同步形成的。当缓冲区已满,生产者在put()操做时,put()内部调用了await()方法,放弃了线程的执行,而后消费者线程执行,调用take()方法,take()内部调用了signal()方法,通知生产者线程能够执行,导致在消费者的println()还没运行的状况下生产者的println()先被执行,因此有了输出不匹配的状况。
对于BlockingQueue你们能够放心使用,这可不是它的问题,只是在它和别的对象之间的同步有问题。