首页 > 编程语言 >02_07_Java语音进阶||day07_等待与唤醒案例、线程池、Lambda表达式

02_07_Java语音进阶||day07_等待与唤醒案例、线程池、Lambda表达式

时间:2023-02-26 19:01:01浏览次数:37  
标签:02 Java 07 包子 线程 new 包子铺 public bz


第一章 等待唤醒机制

1.1 线程间通信

  1. 概念:
  • 多个线程在处理同一个资源(包子),但是处理的动作(线程的任务)却不相同。
  1. 比如:
  • 线程A用来生产包子的,线程B用来吃包子的,包子可以理解为同一资源,线程A与线程B处理的动作,一个是生产,一个是消费,那么线程A与线程B之间就存在线程通信问题

02_07_Java语音进阶||day07_等待与唤醒案例、线程池、Lambda表达式_System

1.2 等待唤醒机制

02_07_Java语音进阶||day07_等待与唤醒案例、线程池、Lambda表达式_System_02

  1. 等待与唤醒机制:线程之间的通信
  • 【重点】:有效的利用资源[包子](生产一个包子,吃一个包子,再生产一个包子,再吃一个包子)
  1. 通信:对包子的状态进行判断
* ==没有==包子-->吃货线程唤醒包子铺线程-->吃货线程等待-->包子铺线程做包子-->做好包子-->修改包子的状态==有==
* ==有==包子-->包子铺线程唤醒吃货线程-->包子铺线程等待-->吃货线程吃包子-->修改包子的状态==没有==
* ==没有==包子-->吃货线程唤醒包子铺线程-->吃货线程等待-->包子铺线程做包子-->做好包子-->修改包子的状态==有==
* ...
  1. 等待唤醒中的方法
  • void wait()
  • 在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。
  • void notify()
  • 唤醒在此对象监视器上等待的单个线程
  • 注:会继续执行wait方法之后的代码
  • void notifyAll()
  • 唤醒在此对象监视器上等待的所有线程
  1. 注意事项:
  • 哪怕只通知了一个等待的线程,被通知线程也不能立即恢复执行因为它当初中断的地方是在同步代码块内,而此刻它已经不持有锁,所以它需要再次去获取锁(++很有可能面临其他线程的竞争++),成功后才能在当初调用wait方法之后的地方恢复执行
  1. 总结:
  • 如果能获取锁,线程就从WAITING状态变成RUNNABLE状态
  • 否则,从wait set出来,又进入entry set,线程又从WAITING状态变成BOLCKED状态
  1. 调用wait和notify方法需要注意的细节
  1. wait和notify必须要由同一个锁对象调用
  • 对应的锁对象可以通过notify唤醒使用同一个锁对象调用的wait方法后的线程
  1. wait与notify是属于Object方法
  • 锁对象可以是任意对象,而任意对象的所属类都是继承了Object类的
  1. wait和notify必须要在同步代码块或者是同步方法(同步函数)中使用
  • 必须要通过锁对象调用这2个方法

1.3 等待唤醒机制案例分析

  1. 等待唤醒机制其实就是经典的“生产者与消费者”问题
  2. 1.1中包子案例分析:

1.4 等待唤醒机制代码实现

  • 包子铺类的注意事项:
  • 包子铺线程与吃货线程关系–>通信(互斥)
  • 必须使用同步技术两个线程只能有一个在执行
  • 锁对象必须保证唯一,可以使用包子对象作为锁对象
  • 包子铺这个类和吃货这个类把包子对象作为参数传入进来
  1. 需要在成员位置创建一个变量
  2. 使用带参构造方法,为这个包子变量赋值
//定义一个包子类:BaoZi.java
/*
包子类
属性:


包子的有无状态:true,false
*/
public class BaoZi {
//皮
String pi;
//馅
String xian;
//包子的有无状态:true,false,初始为false
boolean flag = false;

}

//定义一个包子铺类来继承Thread:BaoZiPu.java
public class BaoZiPu extends Thread{
//1. 需要在成员位置创建一个变量
private BaoZi bz;

//2. 使用带参构造方法,为这个包子变量赋值
public BaoZiPu(BaoZi bz) {
this.bz = bz;
}

//重写run方法:生产包子
@Override
public void run() {
//定义一个变量(用来判断生产那种包子)
int count = 0;

//while让包子铺一直生产包子
while (true){

//同步技术,保证两个线程只有一个在执行
synchronized(bz){ //同步代码块的锁对象就用包子对象
//对包子状态进行判断
if(bz.flag == true){
//调用wait方法进入等待状态
try {
bz.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}

//被唤醒之后执行,包子铺生产包子
//交替生产两种包子
if(count % 2 == 0){
//生产 薄皮 三鲜馅包子
bz.pi = "薄皮";
bz.xian = "三鲜馅";
}else {
//生产 冰皮 牛肉大葱馅包子
bz.pi = "冰皮";
bz.xian = "牛肉大葱馅";
}
count++;
System.out.println("包子铺正在生产" + bz.pi + bz.xian + "的包子");

//生产包子需要3秒钟
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//包子铺生产好包子后,将包子状态修改为有
bz.flag = true;
//唤醒吃货线程,让吃货线程吃包子
bz.notify();
System.out.println("包子铺已经生产好了:" + bz.pi + bz.xian + "的包子,吃货可以开始吃了");
}
}
}
}

//定义一个吃货类来继承Thread:ChiHuo.java
public class ChiHuo extends Thread{
//1. 需要在成员位置创建一个变量
private BaoZi bz;

//2. 使用带参构造方法,为这个包子变量赋值
public ChiHuo(BaoZi bz) {
this.bz = bz;
}

//重写线程任务run:吃包子
@Override
public void run() {

//while让吃货一直吃包子
while (true){

//同步技术,保证两个线程只有一个在执行
synchronized(bz) { //同步代码块的锁对象就用包子对象
//对包子的状态进行判断
if(bz.flag == false){
//吃货调用wait方法进入等待状态
try {
bz.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}

//被唤醒之后执行的代码,吃包子
System.out.println("吃货正在吃" + bz.pi + bz.xian + "的包子");
//吃货吃完包子后,将包子状态修改为无
bz.flag = false;
//唤醒包子铺线程,让包子铺生产包子
bz.notify();
System.out.println("吃货已经把:" + bz.pi + bz.xian + "的包子吃完了,包子铺开始生产包子");
System.out.println("=======================");
}
}
}
}

//主方法(测试类):Demo.java
public static void main(String[] args) {
//创建包子对象
BaoZi bz = new BaoZi();
//创建包子铺线程,开启,生产包子
new BaoZiPu(bz).start();
//创建吃货线程,开启,吃包子
new ChiHuo(bz).start();
}

//结果:
包子铺正在生产薄皮三鲜馅的包子
包子铺已经生产好了:薄皮三鲜馅的包子,吃货可以开始吃了
吃货正在吃薄皮三鲜馅的包子
吃货已经把:薄皮三鲜馅的包子吃完了,包子铺开始生产包子
=======================
包子铺正在生产冰皮牛肉大葱馅的包子
包子铺已经生产好了:冰皮牛肉大葱馅的包子,吃货可以开始吃了
吃货正在吃冰皮牛肉大葱馅的包子
吃货已经把:冰皮牛肉大葱馅的包子吃完了,包子铺开始生产包子
=======================
包子铺正在生产薄皮三鲜馅的包子
...

第二章 线程池

2.1 线程池的概念和原理

  1. 线程池的思想概念

02_07_Java语音进阶||day07_等待与唤醒案例、线程池、Lambda表达式_java_03

2.2 线程池的概念和原理

  1. 概念:
  • 线程池:其实就是一个容纳多个线程的容器【!】,其中的线程可以反复使用省去了频繁创建线程对象的操作无需反复创建线程而消耗过多的资源
  1. 线程池的底层原理:
  • 当程序第一次启动的时候,创建多个线程,保存到一个集合中
  • 当我们想要使用线程的时候,就可以从集合中取出来线程使用
  • Thread t = list.remove(0); //返回的是被移除的元素,(线程只能被一个任务使用
  • Thread t = linked.removeFirst(0);
  • 当我们使用完线程,需要把线程归还给线程池
  • list.add(t);
  • linked.addLast(t);
  • 常用LinkedList集合
  • 注:JDK1.5之后,内置了线程池,可以直接使用,无需自己写2.3会讲【重点】
  1. 线程池工作原理:
  • 注:如果有5个任务,3个线程,如果线程池中无空闲线程时
  • 任务等待执行
  • 等待其他某个任务执行完毕后,归还线程到线程池
  • 再从线程池中获取线程,执行任务
  1. 线程池的优点:
  1. 降低资源消耗
  2. 提高响应速度
  3. 提高线程的可管理性

2.3 线程池的使用

  1. 线程池:JDK1.5之后提供的
  2. java.util.concurrent.Executors:线程池的工厂类,用来生成线程池
  3. Executors类中的静态方法

    标签:02,Java,07,包子,线程,new,包子铺,public,bz
    From: https://blog.51cto.com/u_15980166/6086817

相关文章