当前位置: 首页 > news >正文

学习日记-day18-5.28

完成目标:

继续学习下半段java课程

知识点:

1.多线程_优先级&守护&礼让&插入线程方法
void setPriority(int newPriority)   -> 设置线程优先级,优先级越高的线程,抢到CPU使用权的几率越大,但是不是每次都先抢到int getPriority()  -> 获取线程优先级void setDaemon(boolean on)  -> 设置为守护线程,当非守护线程执行完毕,守护线程就要结束,但是守护线程也不是立马结束,当非守护线程结束之后,系统会告诉守护线程人家结束了,你也结束吧,在告知的过程中,守护线程会执行,只不过执行到半路就结束了static void yield() -> 礼让线程,让当前线程让出CPU使用权void join() -> 插入线程或者叫做插队线程    ===================================================================================
### 1.线程优先级public class MyThread1 extends Thread{@Overridepublic void run() {for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().getName()+"执行了......"+i);}}
}public class Test01 {public static void main(String[] args) {MyThread1 t1 = new MyThread1();t1.setName("金莲");MyThread1 t2 = new MyThread1();t2.setName("阿庆");/*获取两个线程的优先级MIN_PRIORITY = 1 最小优先级 1NORM_PRIORITY = 5 默认优先级 5MAX_PRIORITY = 10 最大优先级 10*///System.out.println(t1.getPriority());//System.out.println(t2.getPriority());//设置优先级t1.setPriority(1);t2.setPriority(10);t1.start();t2.start();}
}========================================================================### 2.守护线程public class Test01 {public static void main(String[] args) {MyThread1 t1 = new MyThread1();t1.setName("金莲");MyThread2 t2 = new MyThread2();t2.setName("阿庆");//将t2设置成守护线程t2.setDaemon(true);t1.start();t2.start();}
}public class MyThread1 extends Thread{@Overridepublic void run() {for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().getName()+"执行了......"+i);}}
}public class MyThread2 extends Thread{@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName()+"执行了..."+i);}}
}===============================================================================### 3.礼让线程场景说明:如果两个线程一起执行,可能会执行一会儿线程A,再执行一会线程B,或者可能线程A执行完毕了,线程B在执行那么我们能不能让两个线程尽可能的平衡一点 -> 尽量让两个线程交替执行
注意:只是尽可能的平衡,不是绝对的你来我往,有可能线程A线程执行,然后礼让了,但是回头A又抢到CPU使用权了    public class Test01 {public static void main(String[] args) {MyThread1 t1 = new MyThread1();t1.setName("金莲");MyThread1 t2 = new MyThread1();t2.setName("阿庆");t1.start();t2.start();}
}public class MyThread1 extends Thread{@Overridepublic void run() {for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().getName()+"执行了......"+i);Thread.yield();}}
}### 4.插入线程public class MyThread1 extends Thread{@Overridepublic void run() {for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().getName()+"执行了......"+i);}}
}public class Test01 {public static void main(String[] args) throws InterruptedException {MyThread1 t1 = new MyThread1();t1.setName("金莲");t1.start();/*表示把t1插入到当前线程之前,t1要插到main线程之前,所以当前线程就是main线程*/t1.join();for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().getName()+"执行了......"+i);}}
}

知识点

核心内容

重点

setPriority()方法

设置线程优先级(1-10),优先级越高抢到CPU使用权的几率越大

默认优先级为5,效果不绝对明显

getPriority()方法

获取线程当前优先级

需注意Thread类中定义的MIN_PRIORITY(1)、NORM_PRIORITY(5)、MAX_PRIORITY(10)常量

setDaemon()方法

将线程设置为守护线程(当所有非守护线程结束时自动终止)

非立即终止,存在系统通知延迟执行现象

yield()方法

礼让线程(让出当前CPU使用权)

非绝对平衡,仍可能出现连续执行情况

join()方法

插队线程(将指定线程插入当前线程前执行)

需注意抛出的InterruptedException异常处理

线程优先级实战

通过t1.setPriority(1)和t2.setPriority(10)演示优先级差异

实际运行效果不明显,需注意CPU调度的随机性

守护线程应用场景

QQ文件传输线程设置为守护线程(当聊天窗口关闭时自动终止传输)

需理解守护线程的生命周期绑定特性

线程调度机制

CPU时间片轮转调度的随机性和不确定性

礼让后仍可能抢到CPU,非完全交替执行

2.多线程_创建线程方式2_实现Runnable接口
1.创建类,实现Runnable接口
2.重写run方法,设置线程任务
3.利用Thread类的构造方法:Thread(Runnable target),创建Thread对象(线程对象),将自定义的类当参数传递到Thread构造中 -> 这一步是让我们自己定义的类成为一个真正的线程类对象
4.调用Thread中的start方法,开启线程,jvm自动调用run方法    public class Test01 {public static void main(String[] args) {MyRunnable myRunnable = new MyRunnable();/*Thread(Runnable target)*/Thread t1 = new Thread(myRunnable);//调用Thread中的start方法,开启线程t1.start();for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().getName()+"...执行了"+i);}}
}public class MyRunnable implements Runnable{@Overridepublic void run() {for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().getName()+"...执行了"+i);}}
}## 两种实现多线程的方式区别1.继承Thread:继承只支持单继承,有继承的局限性
2.实现Runnable:没有继承的局限性, MyThread extends Fu implements Runnable

知识点

核心内容

重点

创建多线程的第二种方式

实现Runnable接口

第二种方式与继承Thread类的区别

实现Runnable接口步骤

1. 创建一个类实现Runnable接口;

2. 重写run方法设置线程任务;

3. 利用Thread类的构造方法,将实现类对象传入,创建线程对象;

4. 调用Thread对象的start方法开启线程

步骤的顺序和具体操作

Runnable与Thread的关系

Runnable是接口,Thread是类;

实现Runnable接口可以避免继承的局限性

Runnable接口与Thread类的区别和联系

两种方式的区别

继承Thread类是单继承,有局限性;

实现Runnable接口可以同时继承其他父类

继承Thread与实现Runnable的对比

实现Runnable接口的优势

可以避免单继承的局限性,使自定义线程类可以同时继承其他父类

实现Runnable接口的具体优势

3.多线程_匿名内部类创建多线程
> 严格意义上来说,匿名内部类方式不属于创建多线程方式其中之一,因为匿名内部类形式建立在实现Runnable接口的基础上完成的匿名内部类回顾: 1.new 接口/抽象类(){重写方法}.重写的方法();2.接口名/类名 对象名 = new 接口/抽象类(){重写方法}对象名.重写的方法();public class Test02 {public static void main(String[] args) {/*Thread(Runnable r)Thread(Runnable target, String name) :name指的是给线程设置名字*/new Thread(new Runnable() {@Overridepublic void run() {for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().getName()+"...执行了"+i);}}},"阿庆").start();new Thread(new Runnable() {@Overridepublic void run() {for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().getName()+"...执行了"+i);}}},"金莲").start();}
}

知识点

核心内容

重点

匿名内部类创建多线程

通过new Thread(Runnable)构造方法实现,匿名内部类重写run()方法

严格来说不属于独立创建方式,本质仍是实现Runnable接口

匿名内部类语法结构

1. 接口/抽象类+{}重写方法;

2. 接口名 对象名=匿名类实例

需区分匿名对象与带名对象的调用方式

线程命名对比

标准方式:MyRunnable类实现接口;

匿名方式:new Thread(Runnable, "线程名")

构造方法重载:Thread(Runnable target, String name)

代码实现对比

原始方式: Thread t = new Thread(new MyRunnable());

匿名方式: new Thread(new Runnable(){...})

匿名类直接替代MyRunnable类定义

执行效果验证

两种方式均可通过start()启动线程;

示例演示循环输出语句

匿名内部类代码更紧凑但复用性差

4.多线程_线程安全问题_同步代码块
public class MyTicket implements Runnable{//定义100张票int ticket = 100;@Overridepublic void run() {while(true){if (ticket>0){System.out.println(Thread.currentThread().getName()+"买了第"+ticket+"张票");ticket--;}}}
}public class Test01 {public static void main(String[] args) {MyTicket myTicket = new MyTicket();Thread t1 = new Thread(myTicket, "赵四");Thread t2 = new Thread(myTicket, "刘能");Thread t3 = new Thread(myTicket, "广坤");t1.start();t2.start();t3.start();}
}

解决线程安全问题的第一种方式(使用同步代码块) 

1.格式:synchronized(任意对象){线程可能出现不安全的代码}
2.任意对象:就是我们的锁对象
3.执行:一个线程拿到锁之后,会进入到同步代码块中执行,在此期间,其他线程拿不到锁,就进不去同步代码块,需要在同步代码块外面等待排队,需要等着执行的线程执行完毕,出了同步代码块,相当于释放锁了,等待的线程才能抢到锁,才能进入到同步代码块中执行public class MyTicket implements Runnable{//定义100张票int ticket = 100;//任意new一个对象Object obj = new Object();@Overridepublic void run() {while(true){try {Thread.sleep(100L);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (obj){if (ticket>0){System.out.println(Thread.currentThread().getName()+"买了第"+ticket+"张票");ticket--;}}}}
}public class Test01 {public static void main(String[] args) {MyTicket myTicket = new MyTicket();Thread t1 = new Thread(myTicket, "赵四");Thread t2 = new Thread(myTicket, "刘能");Thread t3 = new Thread(myTicket, "广坤");t1.start();t2.start();t3.start();}
}

知识点

核心内容

关键细节

代码示例

线程安全问题定义

多线程访问共享资源导致数据异常的现象

需区分"线程安全"与"线程不安全"的表述差异

if(ticket>0){ System.out.println(...); ticket--; }

线程安全问题案例

火车票超卖问题:三个购票渠道同时访问同一票号

资源竞争时机:在ticket--执行前发生线程切换

[完整票务系统代码示例]

同步代码块原理

通过synchronized(锁对象){...}建立临界区

锁对象必须唯一(实例变量 vs 局部变量)

private final Object lock = new Object();

锁机制类比

厕所门锁比喻: - 一个坑位=共享资源; - 门锁=同步机制

错误认知:多把锁仍会导致并发问题

图示说明锁竞争过程

同步代码块执行流程

1. 线程获取锁;

2. 执行临界区代码;

3. 释放锁

关键时序:ticket--必须包含在同步块内

synchronized(lock){ if(ticket>0){...} }

线程安全验证

对比测试: - 无锁版本出现重复票号; - 加锁后票号连续递减

典型错误:在同步块外创建锁对象

[完整对比测试代码]

CPU切换影响

线程不安全根本原因: - 时间片轮转; - 操作非原子性

关键现象:三个线程同时通过ticket>0判断

添加Thread.sleep()放大问题

锁对象要求

必须满足: - 对象唯一性; - 多线程可见性

常见错误:使用new Object()作为局部变量锁

正确做法:成员变量+final修饰

5.多线程_线程安全问题_同步方法
### 1.普通同步方法_非静态1.格式:修饰符 synchronized 返回值类型 方法名(参数){方法体return 结果}
2.默认锁:thispublic class MyTicket implements Runnable{//定义100张票int ticket = 100;@Overridepublic void run() {while(true){try {Thread.sleep(100L);} catch (InterruptedException e) {throw new RuntimeException(e);}//method01();method02();}}/* public synchronized void method01(){if (ticket>0){System.out.println(Thread.currentThread().getName()+"买了第"+ticket+"张票");ticket--;}}*/public void method02(){synchronized(this){System.out.println(this+"..........");if (ticket>0){System.out.println(Thread.currentThread().getName()+"买了第"+ticket+"张票");ticket--;}}}}public class Test01 {public static void main(String[] args) {MyTicket myTicket = new MyTicket();System.out.println(myTicket);Thread t1 = new Thread(myTicket, "赵四");Thread t2 = new Thread(myTicket, "刘能");Thread t3 = new Thread(myTicket, "广坤");t1.start();t2.start();t3.start();}
}===========================================================================### 2.静态同步方法1.格式:修饰符 static synchronized 返回值类型 方法名(参数){方法体return 结果}
2.默认锁:class对象public class MyTicket implements Runnable{//定义100张票static int ticket = 100;@Overridepublic void run() {while(true){try {Thread.sleep(100L);} catch (InterruptedException e) {throw new RuntimeException(e);}//method01();method02();}}/*public static synchronized void method01(){if (ticket>0){System.out.println(Thread.currentThread().getName()+"买了第"+ticket+"张票");ticket--;}}*/public static void method02(){synchronized(MyTicket.class){if (ticket>0){System.out.println(Thread.currentThread().getName()+"买了第"+ticket+"张票");ticket--;}}}
}public class Test01 {public static void main(String[] args) {MyTicket myTicket = new MyTicket();Thread t1 = new Thread(myTicket, "赵四");Thread t2 = new Thread(myTicket, "刘能");Thread t3 = new Thread(myTicket, "广坤");t1.start();t2.start();t3.start();}
}

知识点

核心内容

重点

同步方法解决线程安全问题

同步方法和同步代码块都可以解决线程不安全的问题

同步方法有两种:普通(非静态)和静态

普通同步方法

格式:修饰符 synchronized 返回值类型 方法名(参数) {方法体}

默认锁对象是this

普通同步方法示例

在run方法中调用同步方法,保证线程安全

需要在有线程不安全问题的代码段使用同步方法

非静态同步方法的锁对象

非静态同步方法默认锁对象是this

this代表当前对象,即创建类的实例

静态同步方法

格式:在方法前加static和synchronized

默认锁对象是class对象

静态同步方法示例

静态同步方法不能直接访问非静态成员,需要将相关成员也声明为静态

静态同步方法保证类级别的线程安全

StringBuilder与StringBuffer

StringBuilder线程不安全,StringBuffer线程安全

StringBuffer的方法都是同步方法,因此线程安全

StringBuffer线程安全原因

StringBuffer的方法都加了synchronized关键字

同步方法保证线程安全

StringBuilder与StringBuffer对比

StringBuilder性能高,但线程不安全;StringBuffer线程安全,但性能相对较低

根据使用场景选择合适的类

6.多线程_死锁
public class LockA {public static LockA lockA = new LockA();
}public class LockB {public static LockB lockB = new LockB();
}public class DieLock implements Runnable{private boolean flag;public DieLock(boolean flag) {this.flag = flag;}@Overridepublic void run() {if (flag){synchronized (LockA.lockA){System.out.println("if...lockA");synchronized (LockB.lockB){System.out.println("if...lockB");}}}else{synchronized (LockB.lockB){System.out.println("else...lockB");synchronized (LockA.lockA){System.out.println("else...lockA");}}}}
}public class Test01 {public static void main(String[] args) {DieLock dieLock1 = new DieLock(true);DieLock dieLock2 = new DieLock(false);new Thread(dieLock1).start();new Thread(dieLock2).start();}
}

知识点

核心内容

重点

死锁定义

两个或以上线程因竞争同步锁导致的阻塞现象,若无外力作用无法继续执行

关键特征:互相持有对方所需资源且不释放

死锁产生条件

1. 线程一持有锁一需要锁二;

2. 线程二持有锁二需要锁一;

3. 资源互斥不释放

四个必要条件需同时满足(互斥/占有等待/非抢占/循环等待)

现实案例演示

套间模型: - 小红(钥匙1)与外部门(锁1)/内部门(锁2); - 小绿(钥匙2)与外部门(锁2)/内部门(锁1)

典型错误场景:同步代码块嵌套时钥匙分配冲突

代码实现要点

1. 创建静态锁对象LockA/LockB;

2. 线程类通过flag控制执行不同同步块嵌套;

3. flag=true执行LockA→LockB嵌套,false执行反向嵌套

易错点:线程启动顺序影响死锁发生概率

死锁避免方案

重点标注:避免同步代码块嵌套结构; 替代方案: - 锁排序; - 超时机制; - 资源预分配

破坏循环等待条件的具体措施

7.多线程_线程生命周期

知识点

核心内容

重点

线程状态(生命周期)

线程从创建到终止的6种状态:NEW(新建)、RUNNABLE(可运行)、BLOCKED(锁阻塞)、WAITING(无限等待)、TIMED_WAITING(计时等待)、TERMINATED(终止)

状态转换条件(如start()触发NEW→RUNNABLE);

终止条件(run()结束/异常/stop())

状态转换图

六种状态的互相转换关系: - NEW→RUNNABLE:调用start(); - RUNNABLE→BLOCKED:未抢到锁; - RUNNABLE→TIMED_WAITING:调用sleep()或带超时的wait(); - RUNNABLE→WAITING:调用无参wait()

关键转换路径: WAITING→RUNNABLE需notify()+抢锁成功; 易忽略点:sleep()不释放锁,wait()释放锁

sleep() vs wait()

- sleep():线程睡眠,不释放锁,超时自动唤醒;

- wait():线程等待,释放锁,需notify()唤醒或超时

核心区别:锁的释放行为;

常见错误:混淆两者唤醒后的锁状态(wait()需重新抢锁)

wait()与notify()机制

- wait():使线程进入WAITING状态,需同一锁对象调用notify()唤醒;

- notify():随机唤醒一条等待线程,notifyAll()唤醒所有

强制约束:必须在同步代码块/方法中使用;

易错点:不同锁对象调用导致唤醒失效

锁竞争与状态关联

- 抢锁失败→BLOCKED状态;

- 唤醒后未抢到锁→再次BLOCKED

重点逻辑:唤醒不保证直接执行,需竞争锁

8.多线程_等待唤醒案例分析
/*count和flag可以定义成包装类但是要记得给count和flag手动赋值不然对于本案例来说,容易出现空指针异常*/
public class BaoZiPu {//代表包子的countprivate int count;//代表是否有包子的flagprivate boolean flag;public BaoZiPu() {}public BaoZiPu(int count, boolean flag) {this.count = count;this.flag = flag;}/*getCount 改造成消费包子方法直接输出count*/public void getCount() {System.out.println("消费了..............第"+count+"个包子");}/*setCount 改造成生产包子count++*/public void setCount() {count++;System.out.println("生产了...第"+count+"个包子");}public boolean isFlag() {return flag;}public void setFlag(boolean flag) {this.flag = flag;}
}public class Product implements Runnable{private BaoZiPu baoZiPu;public Product(BaoZiPu baoZiPu) {this.baoZiPu = baoZiPu;}@Overridepublic void run() {while(true){try {Thread.sleep(100L);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (baoZiPu){//1.判断flag是否为true,如果是true,证明有包子,生产线程等待if (baoZiPu.isFlag()==true){try {baoZiPu.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}//2.如果flag为false,证明没有包子,开始生产baoZiPu.setCount();//3.改变flag状态,为true,证明生产完了,有包子了baoZiPu.setFlag(true);//4.唤醒消费线程baoZiPu.notify();}}}
}public class Consumer implements Runnable{private BaoZiPu baoZiPu;public Consumer(BaoZiPu baoZiPu) {this.baoZiPu = baoZiPu;}@Overridepublic void run() {while(true){try {Thread.sleep(100L);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (baoZiPu){//1.判断flag是否为false,如果是false,证明没有包子,消费线程等待if (baoZiPu.isFlag()==false){try {baoZiPu.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}//2.如果flag为true,证明有包子,开始消费baoZiPu.getCount();//3.改变flag状态,为false,证明消费完了,没 有包子了baoZiPu.setFlag(false);//4.唤醒生产线程baoZiPu.notify();}}}
}public class Test01 {public static void main(String[] args) {BaoZiPu baoZiPu = new BaoZiPu();Product product = new Product(baoZiPu);Consumer consumer = new Consumer(baoZiPu);Thread t1 = new Thread(product);Thread t2 = new Thread(consumer);t1.start();t2.start();}
}

知识点

核心内容

重点

多线程等待与唤醒机制

通过生产者-消费者案例演示线程交替执行(生产一个消费一个),需使用wait()、notify()、notifyAll()方法

锁对象必须一致

wait()会释放锁,notify()随机唤醒一条线程

Object类线程控制方法

wait():线程等待并释放锁;

notify():唤醒单个等待线程;

notifyAll():唤醒所有等待线程

方法需通过同步代码块中的锁对象调用

生产者-消费者案例设计

定义count记录包子数量,flag标记包子状态(true有包子/false无包子)

防止连续生产/消费:通过flag判断后调用wait()或notify()

线程通信逻辑

生产者:无包子时生产→修改flag→唤醒消费者;

消费者:有包子时消费→修改flag→唤醒生产者

交替执行关键:wait()与notify()的配对调用

同步代码块作用

加锁防止生产/消费过程被CPU切换打断,但仅加锁无法保证交替执行

需结合等待唤醒机制实现严格轮替

知识点

核心内容

重点

类的创建

创建包子铺、生产者、消费者三个类

类的命名与职责划分

属性定义

包子铺类包含count和flag两个属性

基本数据类型与包装类的选择

构造方法

提供无参构造和有参构造

构造方法的定义与使用

方法改造

getCount改造为消费包的方法,setCount改造为生产包的方法

方法职责的重新划分

线程实现

生产者和消费者分别实现Runnable接口

线程的实现与启动

同步控制

使用同步代码块和锁对象保证线程安全

同步机制的理解与应用

wait/notify使用

生产者与消费者通过wait/notify进行通信

wait/notify的使用场景与限制

测试类编写

编写测试类,启动生产者和消费者线程

测试类的设计与执行

线程交替执行分析

分析生产者和消费者线程的交替执行过程

线程执行流程的理解

锁对象共享

确保生产者和消费者使用同一个锁对象

锁对象的共享与传递

9.多线程_同步方法实现等待唤醒案例
/*count和flag可以定义成包装类但是要记得给count和flag手动赋值不然对于本案例来说,容易出现空指针异常*/
public class BaoZiPu {//代表包子的countprivate int count;//代表是否有包子的flagprivate boolean flag;public BaoZiPu() {}public BaoZiPu(int count, boolean flag) {this.count = count;this.flag = flag;}/*getCount 改造成消费包子方法直接输出count*/public synchronized void getCount() {//1.判断flag是否为false,如果是false,证明没有包子,消费线程等待if (this.flag == false) {try {this.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}//2.如果flag为true,证明有包子,开始消费System.out.println("消费了..............第" + count + "个包子");//3.改变flag状态,为false,证明消费完了,没 有包子了this.flag = false;//4.唤醒生产线程this.notify();}/*setCount 改造成生产包子count++*/public synchronized void setCount() {//1.判断flag是否为true,如果是true,证明有包子,生产线程等待if (this.flag == true) {try {this.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}//2.如果flag为false,证明没有包子,开始生产count++;System.out.println("生产了...第" + count + "个包子");//3.改变flag状态,为true,证明生产完了,有包子了this.flag = true;//4.唤醒消费线程this.notify();}public boolean isFlag() {return flag;}public void setFlag(boolean flag) {this.flag = flag;}
}public class Product implements Runnable{private BaoZiPu baoZiPu;public Product(BaoZiPu baoZiPu) {this.baoZiPu = baoZiPu;}@Overridepublic void run() {while(true){try {Thread.sleep(100L);} catch (InterruptedException e) {throw new RuntimeException(e);}baoZiPu.setCount();}}
}public class Consumer implements Runnable{private BaoZiPu baoZiPu;public Consumer(BaoZiPu baoZiPu) {this.baoZiPu = baoZiPu;}@Overridepublic void run() {while(true){try {Thread.sleep(100L);} catch (InterruptedException e) {throw new RuntimeException(e);}baoZiPu.getCount();}}
}public class Test01 {public static void main(String[] args) {BaoZiPu baoZiPu = new BaoZiPu();Product product = new Product(baoZiPu);Consumer consumer = new Consumer(baoZiPu);Thread t1 = new Thread(product);Thread t2 = new Thread(consumer);t1.start();t2.start();}
}

知识点

核心内容

重点

线程同步方法

将生产线程代码封装到同步方法set中,消费线程代码封装到同步方法get中

同步锁对象一致性(this默认锁)与wait/notify调用逻辑

生产-消费模型改造

通过flag状态判断包子存在与否,wait()使线程等待,notify()唤醒对方线程

flag状态切换时机(生产后true,消费后false)

同步代码块优化

移除显式同步代码块,直接使用同步方法简化结构

隐式锁对象this的作用范围

线程协作验证

输出结果无连续生产/消费,证明同步方法改造有效

wait()与notify()必须持有同一把锁

10.多线程_多等待多唤醒案例
public class Test01 {public static void main(String[] args) {BaoZiPu baoZiPu = new BaoZiPu();Product product = new Product(baoZiPu);Consumer consumer = new Consumer(baoZiPu);new Thread(product).start();new Thread(product).start();new Thread(product).start();new Thread(consumer).start();new Thread(consumer).start();new Thread(consumer).start();}
}/*count和flag可以定义成包装类但是要记得给count和flag手动赋值不然对于本案例来说,容易出现空指针异常*/
public class BaoZiPu {//代表包子的countprivate int count;//代表是否有包子的flagprivate boolean flag;public BaoZiPu() {}public BaoZiPu(int count, boolean flag) {this.count = count;this.flag = flag;}/*getCount 改造成消费包子方法直接输出count*/public synchronized void getCount() {//1.判断flag是否为false,如果是false,证明没有包子,消费线程等待while (this.flag == false) {try {this.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}//2.如果flag为true,证明有包子,开始消费System.out.println("消费了..............第" + count + "个包子");//3.改变flag状态,为false,证明消费完了,没 有包子了this.flag = false;//4.唤醒所有等待线程this.notifyAll();}/*setCount 改造成生产包子count++*/public synchronized void setCount() {//1.判断flag是否为true,如果是true,证明有包子,生产线程等待while (this.flag == true) {try {this.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}//2.如果flag为false,证明没有包子,开始生产count++;System.out.println("生产了...第" + count + "个包子");//3.改变flag状态,为true,证明生产完了,有包子了this.flag = true;//4.唤醒所有等待线程this.notifyAll();}public boolean isFlag() {return flag;}public void setFlag(boolean flag) {this.flag = flag;}
}public class Product implements Runnable{private BaoZiPu baoZiPu;public Product(BaoZiPu baoZiPu) {this.baoZiPu = baoZiPu;}@Overridepublic void run() {while(true){try {Thread.sleep(100L);} catch (InterruptedException e) {throw new RuntimeException(e);}baoZiPu.setCount();}}
}public class Consumer implements Runnable{private BaoZiPu baoZiPu;public Consumer(BaoZiPu baoZiPu) {this.baoZiPu = baoZiPu;}@Overridepublic void run() {while(true){try {Thread.sleep(100L);} catch (InterruptedException e) {throw new RuntimeException(e);}baoZiPu.getCount();}}
}

知识点

核心内容

重点

多线程生产消费模型

通过notifyAll()和while循环解决多线程下的连续生产/消费问题

notify()与notifyAll()的唤醒机制差异

线程等待与唤醒机制

wait()释放锁并进入等待,notifyAll()唤醒所有等待线程竞争锁

if改为while的必要性(避免虚假唤醒)

线程安全控制

通过flag标志位控制生产/消费的交替执行

多线程下flag的原子性判断与竞态条件

锁竞争问题

六条线程(3生产+3消费)通过synchronized抢锁执行

连续生产/消费的根本原因(未循环检查状态)

解决方案对比

notifyAll()+while vs notify()+if的稳定性差异

notify()可能导致线程无限等待

http://www.xdnf.cn/news/702073.html

相关文章:

  • 央国企迁移国产数据库:数据迁移5步法与4项管理准则
  • GATED DELTA NETWORKS : IMPROVING MAMBA 2 WITH DELTA RULE
  • 【AI算法工程师面试指北】小球检测问题
  • 【Python-Day 19】函数的回响:深入理解 `return` 语句与返回值
  • 融智学视域下的多时空统一框架与信智序位法则
  • 基于CATIA参数化圆锥建模的自动化插件开发实践——NX建模之圆锥体命令的参考与移植(三)
  • 图神经网络在信息检索重排序中的应用:原理、架构与Python代码解析
  • ORB-SLAM2学习笔记:ORBextractor::operator()函数的逐行解析
  • 应用宝的NotificationManagerService_post_com.tencent.android.qqdownloader持锁现象
  • 涨薪技术|0到1学会性能测试第87课-Webservice接口性能测试
  • (nice!!!)(LeetCode 每日一题) 3372. 连接两棵树后最大目标节点数目 I (贪心+深度优先搜索dfs)
  • GPU时间与transformer架构计算量分析
  • qemu安装risc-V 64
  • springboot配置mybatis debug的sql日志输出
  • DelayQueue源码解析
  • 《活法》
  • Python实例题:Python实现FTP弱口令扫描器
  • 如何去除文章的AI痕迹2025新方法
  • DeepSeek 工作应用深度指南
  • 二叉树的锯齿形层序遍历——灵活跳跃的层次结构解析
  • 第十一节:第三部分:异常:异常的两种处理方式
  • 【Unity】自动生成围绕模型的路径点
  • 企业应如何构建用户画像系统
  • C语言Day9:C语言类型转换规则
  • Linux Crash工具全解:内核崩溃分析的一切
  • shell脚本总结11
  • 华为OD机试真题——矩形绘制(2025A卷:200分)Java/python/JavaScript/C/C++/GO最佳实现
  • 数据库表与实体类设计
  • 中望CAD与AutoCAD的SWOT对比分析(基于2025线上发布会观察与行业数据)
  • 阿里云云效对接SDK获取流水线制品