生产者消费者模型
生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队 列来进行通讯,生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从 阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力
与生产者消费者模型相关的两个方法: wait()与notify()方法
wait()-痴汉方法
wait()就是使线程停止运行,会释放对象锁。(运行态---->阻塞态)
1.wait()方法会使当前线程调用该方法后进行等待,并将该线程置入锁对象的等待队列中,直到接到通知或被中断为止。
2.wait()方法只能在同步方法或同步代码块中调用,如果调用wait()时没有适当的锁,会抛出异常。
3.wait()方法执行后,当前线程释放锁,其他线程可以竞争该锁。
wait()之后的线程继续执行有两种方法:
1.调用该对象的notify()方法唤醒等待线程
2.线程等待周期等待时调用interrupt()中断该线程
wait(long time)
:如果到了预计时间还没被唤醒,线程将继续执行。
notify()
1.notify()方法也必须在同步方法或同步代码块中调用,用来唤醒等待在该对象上的线程。如果有多个线程等待,则任意挑选一个线程唤醒。
2.notify()方法执行后,唤醒线程不会立即释放对象锁,要等待唤醒线程全部执行完毕后才释放对象锁。
notifyAll()唤醒所有在该对象上等待的线程
每个锁对象都有两个队列,一个是就绪队列,一个是阻塞队列。就绪队列存储了将要获得锁的线程,阻塞队列存储了被 阻塞的线程。一个线程被唤醒后,才会进入就绪队列,等待CPU的调度;反之,一个线程被wait后,就会进入阻塞队列,等 待下一次被唤醒。
public class WaitTest {
public static void main(String[] args) {
//对象在主方法中创建,因为缓冲池只有一个
Object object=new Object();
Mythread waitThread=new Mythread(true,object);
Mythread notifyThread1=new Mythread(false,object);
Thread thread=new Thread(waitThread,"wait线程");
Thread thread2=new Thread(notifyThread1,"notify线程");
thread.start();
thread2.start();
System.out.println("main方法结束");
}
}
class Mythread implements Runnable {
private boolean flag;
private Object object;
public Mythread(boolean flag, Object object) {
this.flag = flag;
this.object = object;
}
public void waitMethod() {
synchronized (object) {
while (true) {
System.out.println("wait方法开始" + Thread.currentThread().getName());
try {
object.wait();
System.out.println("wait方法结束" + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public void notifyMethod() {
synchronized (object) {
System.out.println("notify方法开始" + Thread.currentThread().getName());
object.notify();
System.out.println("notify方法结束" + Thread.currentThread().getName());
}
}
@Override
public void run() {
if (flag) {
this.waitMethod();
} else {
this.notifyMethod();
}
}
}
线程阻塞的情况:
1.线程调用 sleep方法,主动放弃占用的处理器资源。
2. 线程调用了阻塞式IO方法,在该方法返回前,该线程被阻塞。
3. 线程试图获得一个同步监视器,但该同步监视器正被其他线程所持有。
4.线程等待某个通知。
5. 程序调用了 suspend方法将该线程挂起。此方法容易导致死锁,尽量避免使用该方法
生产者消费者模型
生产者与消费者不进行直接通信,而是通过阻塞队列间接通信,通过判断队列中商品,若是满了则生产者是否进行等待,队列中商品为空消费者提醒.
或是,当商品池空了消费者就进行等待,商品池有商品就提醒.
因为线程的同步是通过对对象加锁来控制的,因此生产者与消费者共同锁住同一个对象来进行"沟通".
public class PoducerCustomer {
public static void main(String[] args) {
Queue<Goods> goods=new LinkedList<>();
AtomicInteger au=new AtomicInteger(0);
Object monitor=new Object();
//创建生产者
Producer producer=new Producer(goods,monitor,au);
Producer producer2=new Producer(goods,monitor,au);
//创建消费者
Customer customer = new Customer(goods, monitor);
//创建线程启动生产者与消费者
new Thread(producer).start();
new Thread(producer2).start();
new Thread(customer).start();
}
}
class Goods{
private final String name;
public Goods(String name) {
this.name = name;
}
@Override
public String toString() {
return "Goods{" +
"name='" + name + '\'' +
'}';
}
}
class Producer implements Runnable{
//生产者将生产的商品放入缓冲池,
//因为缓冲池是共有的,因此不能new一个实例化对象,
// 只能通过传参的方式传入生产者的实例化对象中
private final Queue<Goods> goods;
// 利用对监听器对象上锁保证放入缓冲池的同步性
//因为生产者与消费者通过对同一个对象进行控制来进行沟通,
// 因此只能有一个实例化对象,在主类的主方法中产生
private final Object monitor;
//对商品进行编号,利用原子整型保证线程安全,在多线程中不能用i++
private final AtomicInteger atomicInteger;
public Producer(Queue<Goods> goods, Object monitor, AtomicInteger atomicInteger) {
this.goods = goods;
this.monitor = monitor;
this.atomicInteger = atomicInteger;
}
@Override
public void run() {
while(true){
try {
//通过睡眠时间控制生产者与消费者的速度
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (monitor){
//设定缓冲池到达10个时,生产者wait()
//满等待
if(this.goods.size()==10){
try {
this.monitor.wait();
System.out.println("商品池满了,休息一会");
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
//若未满10个,向线程池中添加商品
Goods goods=new Goods(String.valueOf(atomicInteger.addAndGet(1)));
this.goods.add(goods);
System.out.println(Thread.currentThread().getName()+"生产"+goods);
}
}
}
}
}
class Customer implements Runnable{
//缓冲队列
private final Queue<Goods> goods;
private final Object monitor;
public Customer(Queue<Goods> goods, Object monitor) {
this.goods = goods;
this.monitor = monitor;
}
@Override
public void run() {
while (true){
try {
//通过睡眠时间控制生产者与消费者的速度
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (monitor){
//空唤醒
if(goods.isEmpty()){
System.out.println("商品池空了,快来生产呀~~");
monitor.notify();
}else {
System.out.println("消费者消费"+goods.poll());
}
}
}
}
}