写点什么

Java 多线程 —— 生产者消费者问题

  • 2021 年 11 月 11 日
  • 本文字数:1909 字

    阅读完需:约 6 分钟

解决方法:synchronized,同步代码块


当生产者给对象赋值的时候,消费者无法操作对象取值,避免了生产者只给对象的 name 赋值,而消费者已经把 name 和 age 的值都取走的情况


  1. 线程抢到了 CPU 后,可以在很短的时间内运转多次。


这样可能出现只有一个货架,生产者生产完商品后,还没有等消费者来取,就不断的生产新的商品来替代货架上的旧商品的情况。或者消费者已经将商品取走,还在对着空的货架不断取商品的情况。


解决办法:加入等待唤醒机制


等待唤醒机制通过 Object 类中的三个方法来实现(注意是 Object,不是 Thread):


void wait() 导致当前线程等待,直到另一个线程调用该对象的 notify()方法或 notifyAll()方法。


void notify() 唤醒正在等待对象监视器的单个线程。


void notifyAll() 唤醒正在等待对象监视器的所有线程。


这三个方法的具体使用是:


  1. wait()


在调用 wait()之前,线程必须要获得相应对象的对象监视器锁(这里是 Student 对象),所以只能在同步方法或同步块中调用 wait()方法。对象调用 wait()方法后,当前线程会立即释放锁,然后进入休眠状态,直到被 notify()唤醒或者被中断。同时线程的执行会在 wait()语句处停止,直到再次获得锁,当前线程才能从 wait()方法处成功返回,然后继续执行下面的代码。另外,被释放的锁会立刻被其他等待锁的线程抢夺,抢到锁的线程开始执行同步代码块。


  1. notify()


notify()同样需要获得锁,并且只能在同步方法或同步代码块中调用。当前线程调用 notify()后,会唤醒之前 wait()后陷入休眠的线程,使其从等待队列进入同步队列,获取当前线程的锁,并且从之前 wait()语句处继续执行(这里要求对象锁的对象要一致,才会去唤醒)。当等待唤醒的线程较多时,会根据机制随机挑选一个线程唤醒。当前线程调用 notify()方法后不会立刻释放锁,而是继续执行,直到执行结束退出同步方法或同步代码块时,才会释放锁。


  1. notifyAll()


notifyAll()与 notify()的工作方式大致相同,不同的是等待线程较多时,notify()会随机挑


【一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义】
浏览器打开:qq.cn.hn/FTf 免费领取
复制代码


选一个线程通知,而 notifyAll()会将所有具有相同对象锁的线程全部唤醒,让这些线程争抢锁。


这里注意 wait()与 notify(),notifyAll()联系的桥梁是相同的对象,即 synchronized(对象){ },不同的线程之间,括号里面的对象相同


生产者线程


package test.MyThread.ProductDemo;


public class SetThread implements Runnable{


Student s ;


int x = 0;


public SetThread(Student s){


this.s = s;


}


@Override


public void run() {


while(true){


synchronized(s){


if(s.flag){


//说明已经给学生对象赋值,应该等待消费者来获取


try {


s.wait();


} catch (InterruptedException e) {


e.printStackTrace();


}


}else{


//这里是两类商品,当 x%2==0 的时候上架商品水果,否则上架商品面包


//这里放在学生类里面想当于一个叫水果的学生,一个叫面包的学生


if(x%2==0){


s.setName("水果");


s.setAge(18);


}else{


s.setName("面包");


s.setAge(15);


}


x++;


//赋值完毕,将状态转为 true,并通知消费者来取值


s.flag = true;


s.notify();


}


}


}


}


}


消费者线程


package test.MyThread.ProductDemo;


public class GetThread implements Runnable{


Student s ;


public GetThread(Student s){


this.s = s;


}


@Override


public void run() {


while(true){


synchronized(s){


//s.flag 为 false 时,!s,flag 为 true,运行 if 语句


if(!s.flag){


try {


s.wait();


} catch (InterruptedException e) {


e.printStackTrace();


}


}


System.out.println(s.name + "---" + s.age);


s.notify();


s.flag = false;


}


}


}


}


s.flag=true 说明有商品,s.flag=false 说明没有商品


这里没有商品的时候应该用锁对象调用 wait()方法,线程释放锁对象,并且代码的执行停留在这一步


锁对象被释放后,会被生产者抢夺,而消费者不会再抢夺锁对象


然后生产者生产商品完毕后,用 notify()方法唤醒消费者,并且将锁对象给消费者


调用 notify()方法后不会立刻释放锁对象,而是等生产者将同步代码块全部运行完之后才会释放给消费者


这样生产者会继续从头运行,检测是否有商品,如果商品还没有被消费者消费,那么就进入等待状态


最后消费者接着执行代码,消费商品,消费完毕后用 notify()方法唤醒生产者生产商品


package test.MyThread.ProductDemo;


public class StudentDemo {


public static void main(String[] args) {


Student s = new Student();


SetThread s1 = new SetThread(s);


GetThread g1 = new GetThread(s);


Thread t1 = new Thread(s1);


Thread t2 = new Thread(g1);


t1.start();


t2.start();


}


}


运行一下,结果为



生产者不断的给学生类的 name 和 age 属性赋予新的值,然后消费者再不断的获取这些值并打印输出。

评论

发布
暂无评论
Java 多线程 —— 生产者消费者问题