目录
生产者消费者问题
生产者消费者问题,也称有限缓冲问题,是一个线程同步问题的经典案例。
- 在缓冲区(仓库)为空时,消费者不能进行消费
- 在缓冲区(仓库)为满时,生产者不能进行生产
- 在一个线程进行生产或者消费时,其他线程不能进行消费或者生产操作,即保持线程间的同步
问题解决
因为需要保持线程间的同步,即一个线程消费(或生产)完,其它线程才能争抢CPU资源:生产者线程在生产之前,需要等待直至获取自己所需的信号量之后(即仓库为空),才会进行生产的操作;同样,对于消费者线程,在消费之前需要等待直到没有线程在访问共享区(缓冲区),再进行消费的操作,之后再解锁并唤醒其他可用阻塞线程。
实现代码
篇幅有限,便在网上找了篇文章。主要是因为懒 ¬o( ̄- ̄メ)
原理
在Java对象中有两种池:
锁池---------->sychronized
等待池-------->wait(),notify(),notifyAll()
- 如果一个线程调用了某个对象的wait()方法,那么该线程进入到该对象的等待池中(并且已经将锁释放)
- 如果未来某一时刻,另外一个线程调用了相同对象的notify()方法或者notifyAll()方法,那么该等待池中的线程就会被唤醒,然后进入到对象的锁池里面去获得该对象的锁
- 如果获得锁成功后,那么该线程就会沿着wait()方法之后的路径继续执行。注意是沿着wait()方法之后
wait()方法和notify()方法,是必须放在同步方法或者同步代码块中才有效的.因为在同步的基础上进行线程的通信才是最有效的
线程生命完整周期图
Condition
Oralce官方文档:Condition (Java Platform SE 8 )
Condition是在Java 1.5中才出现的,它用来替代传统的Object的wait()、notify()实现线程间的协作,相比使用Object的wait()、notify(),使用Condition1的await()、signal()这种方式实现线程间协作更加安全和高效。
- 它的更强大的地方在于:能够更加精细的控制多线程的休眠与唤醒。对于同一个锁,我们可以创建多个Condition,在不同的情况下使用不同的Condition
- 一个Condition包含一个等待队列。一个Lock可以产生多个Condition,所以可以有多个等待队列
- 在Object的监视器模型上,一个对象拥有一个同步队列和等待队列,而Lock(同步器)拥有一个同步队列和多个等待队列
- Object中的wait(),notify(),notifyAll()方法是和"同步锁"(synchronized关键字)捆绑使用的;而Condition是需要与"互斥锁"/"共享锁"捆绑使用的
- 调用Condition的await()、signal()、signalAll()方法,都必须在lock保护之内,就是说必须在lock.lock()和lock.unlock之间才可以使用
- Conditon中的await()对应Object的wait();
- Condition中的signal()对应Object的notify();
- Condition中的signalAll()对应Object的notifyAll()
三种方法
void await() throws InterruptedException { }
造成当前线程在接到信号或被中断之前一直处于等待状态
与此 Condition 相关的锁以原子方式释放,并且出于线程调度的目的,将禁用当前线程,且在发生以下四种情况之一之前,当前线程将一直处于休眠状态:
- 其他某个线程调用此 Condition 的 signal() 方法,并且碰巧将当前线程选为被唤醒的线程
- 其他某个线程调用此 Condition 的 signalAll() 方法
- 其他某个线程中断当前线程,且支持中断线程的挂起
- 发生“虚假唤醒”
在所有情况下,在此方法可以返回当前线程之前,都必须重新获取与此条件有关的锁。在线程返回时,可以保证它保持此锁
void signal() {}
唤醒一个等待线程
如果所有的线程都在等待此条件,则选择其中的一个唤醒。在从 await 返回之前,该线程必须重新获取锁
void signalAll() {}
唤醒所有等待线程
如果所有的线程都在等待此条件,则唤醒所有线程。在从 await 返回之前,每个线程都必须重新获取锁