javaEE 多线程4

发布时间:2026/10/22 13:01:17

javaEE 多线程4 wait()必须在synchronized同步块中使用以确保线程安全。它使当前线程阻塞并释放锁直到其他线程调用notify()或notifyAll()唤醒。为防止唤醒过早需配合标记变量如w判断条件是否满足。多个wait()线程可被notify()唤醒但建议使用notifyAll()避免死锁。阻塞队列、线程池、定时器等均基于生产者-消费者模型实现提升并发效率与资源复用。wait notitywait 等待阻塞wait 必须加锁才能使用解释synchronized锁对象{//入锁锁对象.wait//阻塞 等待唤醒}//出锁例子public static Object qnew Object();public static Boolean wfalse;public static void main(String[] args) {Thread anew Thread(()-{System.out.println(a线程wait前);while (!w) {synchronized (q) {try {q.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}}System.out.println(a线程wait后);});a.start();synchronized (q){wtrue;q.notify();System.out.println(a唤醒成功);}为什么要有标记因为担心主线程唤醒的时候 子线程还没有执行wait代码 直接唤醒没有任何效果 notity叫醒了 标记不执行wait()的阻塞 跳过while 执行下面的代码多个线程多个wait 一个唤醒notifypublic static void main(String[] args) throws InterruptedException {Thread anew Thread(()-{synchronized (q) {while (!w) {try {System.out.println(1);q.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}}System.out.println(3);});Thread a1new Thread(()-{synchronized (q) {while (!w) {try {System.out.println(2);q.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}}System.out.println(4);});a.start();a1.start();Thread.sleep(10000);//等待两个线程都进入wait()阻塞synchronized (q){wtrue;q.notify();System.out.println(唤醒);}}sleep和wait区别sleep和wait两者都具有沉睡功能两则都可提前唤醒 sleep被Interrupt提前唤醒 实际上是改变标记点wait必须上锁和解锁sleep则不用如果在synchronized锁里 wait会解锁 sleep则不会AllnotifyAllnotify唤醒所有线程Allnotify唤醒在被wait沉睡的所有线程模式单例模式单例模式在某个类 某个程序中 只有一个实例 new单例模式分为饿汉模式和懒汉模式饿汉模式先创建好对象 通过方法获取对象名和对象//饿汉模式 在类加载的时候就创建好了 public static Thread3 anew Thread3(); private Thread3(){ System.out.println(饿汉模式); } //获取到创建好的对象 public static Thread3 a1(){ return a; } //拿到对象的名字 public void test(){ System.out.println(Thread.currentThread().getName() 拿到实例 this); } public static void main(String[] args) { //执行任务 for (int i 0; i 5; i) { int idi; new Thread(()-{ Thread3 aThread3.a1(); a.test(); },线程id).start(); } }懒汉模式在第一次使用时才创建好对象//懒汉模式在第一个使用时创建 而不是类加载的时候 private static Thread3 anull; public static Thread3 a1() { //如果没有创建则创建 创建好了则返回 if (a null) { return new Thread3(); } return a; } private Thread3(){ System.out.println(懒汉模式); } public void test() { System.out.println(Thread.currentThread().getName() 拿到实例 this); } public static void main(String[] args) { for (int i 0; i 5; i) { new Thread(()-{ Thread3 a3Thread3.a1(); a3.test(); }).start(); } }懒汉模式的弊端这个方法里线程是不安全的 线程安全要求操作是原子性的 唯一的其中if和创建对象是两个 如果判断一下正要new 就切换其他线程 也要new这个也解释了我们new了一次 调用一次构造方法 为什么会调用超过一次以上的构造方法就破坏了单例模式 所以这两个操作就要绑定一起解决方法进行加锁操作public static synchronized 类对象进行加锁 Thread3 a1() { //如果没有创建则创建 创建好了则返回 synchronized (a4) { if (a null) { a new Thread3(); } } return a;}这种方法解决了线程的安全性 但是性能大大降低一个线程在执行该方法时不论有没有创建对象 都会先阻塞 等拿到锁的线程先执行完所以我们在没进锁之前先判断是否有没有对象 如果有直接返回 没有则加锁if (a null) { //是否有没有实例 synchronized (a4) { if (a null) { //进一步判断有没有实例 a new Thread3(); } } return a; }a线程进入没有创建实例 拿到了锁 正要创建 此时b线程通过第一层在外面等待 a此时创建好了 b拿到了锁但是此时的a创建好了 则直接返回这时要考虑内存的可见性问题线程要读到主内存 实时更新 进行读操作 避免重排序问题 a加volatilenew Thread3() 3个指令1分配内存2初始化构造方法3把内存首地址赋值给引用变量//懒汉模式在第一个使用时创建 而不是类加载的时候 public static Object a4new Object(); private static volatile Thread3 anull; public static Thread3 a1() { //如果没有创建则创建 创建好了则返回 if (a null) { synchronized (a4) { if (a null) { a new Thread3(); } } } return a; } private Thread3(){ System.out.println(懒汉模式); } public void test() { System.out.println(Thread.currentThread().getName() 拿到实例 this); } public static void main(String[] args) { for (int i 0; i 5; i) { new Thread(()-{ Thread3 a3Thread3.a1(); a3.test(); }).start(); } }阻塞队列由来服务器之间通过直接传输数据 请求和保持 大家都知道有高峰和低峰的时候 难免造成服务器的中断 这时候出现 一个间接工具来 间接传输数据 阻塞队列把阻塞队列包装成服务器程序 单独使用机器来部署叫消息队列消费生产模型工作原理消费者 当队列满了开始阻塞 消费者去执行生产者 当队列空了开始阻塞 生产者去生产消费生产者模型的模拟实现package Book3; import java.util.concurrent.BlockingDeque; /** * Created with IntelliJ IDEA. * Description: * User: 28931 * Date: 2026-10-18 * Time: 13:59 */ public class Tread4 { //模拟实现阻塞队列 public static int[] a; //个数 public static int size; //入队列计数 public static int head; //出队列计数 public static int w; //初始化 public Tread4(int b){ anew int[b]; } //入队列 private synchronized void put(int a1) throws InterruptedException { //while循环是因为多线程 如果一次下一个线程就判断不了 while (sizea.length){ System.out.println(队列满了 开始阻塞 出队列); //等完成了一个载入 又会回到这里 this.wait(); } a[head]a1; head; size; //如果满了 满了就会清0 消费者就会执行 if(heada.length){ head0; } this.notifyAll(); } //出队列 public synchronized int peek() throws InterruptedException { while (size0){ System.out.println(队列空 开始阻塞 入队列); this.wait(); } int qa[w]; w; //如果获取的数已经超了就会重新回到0 if(wa.length){ w0; } size--; this.notifyAll(); return q; } //获取个数 public int getSize(){ return size; } public static void main(String[] args) { Tread4 Enew Tread4(3); //生产者 Thread producer new Thread(() - { int i 1; while (true) { try { E.put(i); System.out.println(✅生产 i 当前数量 E.getSize()); i; Thread.sleep(800); } catch (InterruptedException e) { e.printStackTrace(); } } }); //消费者 Thread consumer new Thread(() - { int i1; while (true) { try { E.peek(); System.out.println(❌消费 i 当前数量 E.getSize()); Thread.sleep(1200); } catch (InterruptedException e) { e.printStackTrace(); } } }); producer.start(); consumer.start(); } }怎么调节notifyAll来唤醒wait通过while条件 满足则进入 到wait()条件执行下面的条件线程池常量池在java中有独立的内存空间 只需把内容加载到内存 实现创建和销毁我们在使用和销毁进程太慢线程消耗大怎么解决1线程池2协程线程为什么使用线程池比使用线程创建效率高线程是操作系统调用操作系统分为内核和应用程序线程是内核来调度 从应用程序切换到内核 不可控制过程线程池是应用程序的 本身自带的jdk 线程比较安全线程池先创建好几个线程 线程创建好销毁 又返回线程池重新使用使用线程池Threadpoolexecutor方法参数int corepoolsize核心线程 至少有几个线程int maximumpoolsize核心线程非核心 最大线程loog keepAliveTime非核心线程最长存活时间Timeunit unit枚举blockingqueueRunnable工作队列线程池本质也是消费生成模型submit生产线程消费ThreadFactory工厂方法RejectedExecutionHandler handler拒绝策略1ThreadpoolExecutor.AbortPolicy直接抛异常2ThreadPoolExecutor.callerRunsPolicy让调用submit的线程自行执行任务submit(Runnable runnable){if(满了callerRunsPolicy){runnable.run();return ;}queue.put(runnable);return;3ThreadPoolExecutor.DiscardoldestPolicy丢弃最老的任务4ThreadPoolExecutor.DiscardPolicy丢弃当前的任务工厂方法把一个实例化的对象封装成方法 想用的时候直接调方法模拟实现线程池//阻塞队列public BlockingDequeRunnable queue;//线程数量private final int a;//线程标记private volatile Boolean pfalse;//创建好线程public Thread5(int a,int b) {this.a a;queuenew LinkedBlockingDeque(b);for (int i 0; i a; i) {Thread enew Thread(()-{try {//接收任务 没有任务就阻塞 take()方法内有waitRunnable wqueue.take();//run()有notity去唤醒 运行labda表达式中的代码w.run();} catch (InterruptedException ex) {//异常就终止循环ptrue;System.out.println(线程中断Thread.currentThread().getThreadGroup());}}, worker- i);;e.start();}}//调用运行主代码public void sumbit(Runnable runnable) throws InterruptedException {if(p){throw new InterruptedException(线程池关闭 不能提交代码);}queue.put(runnable);}//关闭资源public void set(){ptrue;System.out.println(关闭资源);}public static void main(String[] args) throws InterruptedException {// 构造3个工作线程队列最多放5个任务Thread5 pool new Thread5(3, 5);// 提交10个测试任务for (int i 1; i 10; i) {int taskId i;pool.sumbit(() - {System.out.println(Thread.currentThread().getName() 执行任务 taskId);try {Thread.sleep(1000);} catch (InterruptedException e) {Thread.currentThread().interrupt();}});}Thread.sleep(1000);System.out.println(准备关闭线程池);pool.set();}模拟线程池怎么调用的主函数调用自定义函数sumbit sumbit里调用que.put() 当线程里有队列就执行取出take函数 空了就阻塞 take()函数内有wiat等待 来一个执行一个 run函数里有notity唤醒 执行线程里的任务返回值说明0等于结束130等于中途结束定时器的使用//定时器 Timer anew Timer(); //定时器任务 TimerTask bnew TimerTask() { Override public void run() { System.out.println(1000); } }; //启动定时器 a.schedule(b,1000);模拟实现定时器public class Thread6 implements ComparableThread6 { //任务 public Runnable runnable; //绝对到期时间 public long time; public Thread6(Runnable runnable, long time) { this.runnable runnable; this.time time; } Override public int compareTo(Thread6 o) { // 时间小更早到期排在堆顶 return Long.compare(this.time, o.time); } } class a1 { //管理延时队列存放未到时间的任务 private PriorityBlockingQueueThread6 q new PriorityBlockingQueue(); //工作执行队列时间到的任务交给worker private PriorityBlockingQueueThread6 q1 new PriorityBlockingQueue(); public a1() { Thread manager new Thread(() - { while (true) { try { Thread6 task q.take(); long now System.currentTimeMillis(); if (task.time now) { // ✅ 时间到送入执行队列 q1.put(task); } else { // ✅ 时间未到放回延时队列等待剩余时长 q.put(task); synchronized (q) { q.wait(task.time - now); } } } catch (InterruptedException e) { e.printStackTrace(); break; } } }, manager-thread); manager.setDaemon(true); manager.start(); Thread worker1 new Thread(() - { while (true) { try { Thread6 task q1.take(); // 真正执行业务代码内层捕获任务异常防止worker死掉 try { task.runnable.run(); } catch (Exception ex) { System.out.println(任务执行异常); ex.printStackTrace(); } } catch (InterruptedException e) { e.printStackTrace(); break; } } }, worker-thread); worker1.setDaemon(true); worker1.start(); } //提交定时任务delay 毫秒之后执行 public void schedule(Runnable runnable, long delay) { long targetTime System.currentTimeMillis() delay; q.put(new Thread6(runnable, targetTime)); } public static void main(String[] args) { a1 timer new a1(); System.out.println(main提交任务 System.currentTimeMillis()); timer.schedule(() - { System.out.println(任务A执行线程 Thread.currentThread().getName() 时间 System.currentTimeMillis()); }, 2000); timer.schedule(() - { System.out.println(任务B执行线程 Thread.currentThread().getName() 时间 System.currentTimeMillis()); }, 4000); // 主线程等待不然守护线程跟着直接退出看不到输出 try { Thread.sleep(6000); } catch (InterruptedException e) { e.printStackTrace(); } } }调用思路main方法- a1对象的创建 把所需的线程先创建好 - timer.schedule(() 创建好任务 交给管理ui列管理线程到了时间阻塞 当队列为空 也阻塞调用工作线程 队列空阻塞比较方法由阻塞队列调用另一种模拟“时间轮”利用循环队列 每个元素是“时间单位” 每一个元素是链表 每到一个时间单位 遍历链表 对应的任务性能高不如优先级队列精度好
延伸阅读

更多相关文章

2026/10/22 13:01:17

远程控制软件怎么选?不同人真的适合不同软件

我发现很多人选远控软件的时候,喜欢先问一句:“哪一个最好?”但这个问题其实不太好回答。因为拿它办公、打游戏、做设计,还是管理公司的电脑,最后选出来的产品可能完全不同。所以这次直接按照不同使用需求来看看 UU远程…

2026/10/22 12:56:17

ComfyUI漫画分镜二实战:同Seed角色一致性验证+侧脸特写生成,国漫雨巷第2格

你是不是已经把第 1 格画好了,远景背影、雨巷氛围都对了,但一到第 2 格要画侧脸特写,心里就开始打鼓——同一个 Seed、同一套参数,侧脸会不会又变成另一张脸? 别慌,我也担心过这个,直到我把第 1 格和第 2 格并排放在一起,发现角色的发色、服装、发饰都稳住了,只是视角…

2026/10/22 12:56:17

【零基础学AI】第 15 章课后练习与答案

第 15 章练习与答案🌈 关于《AI 零基础 36 讲》课后训练 📚 与正式讲解配套学习 本篇是《AI 零基础 36 讲》的配套课后训练,与对应章节的正式讲解一起使用。整个系列面向初学者,从日常 AI 使用逐步进入编程、模型训练和大模型应用…

2026/10/22 14:11:52

Python执行原理:从源码到字节码的四级运行机制

1. 这不是“翻译”,而是让代码活起来的现场调度你写完一行print("Hello, World!"),按下回车,屏幕立刻跳出那串字符——这背后没有魔法,只有一套精密、实时、高度协作的运行机制在高速运转。很多人把 Python 叫作“解释型…

2026/10/22 14:11:52

AI“生成视频”的真相:不是视频文件,是浏览器逐帧绘制代码

说实话,我第一次对着某个AI模型说出“帮我生成一个视频”的时候,已经做好了接收一个.mp4文件的十足心理准备。结果它发过来一个压缩包,里面躺着一个HTML文件——那一刻我真有点懵。用浏览器打开,深蓝色的画布上一条金色光带开始缓…

2026/10/22 14:11:52

Agent运行时契约:从混乱编排到可验证、可调度、可恢复

1. 从1379星暴涨说起:这不是又一个“玩具项目”,而是Agent系统工程化的临界点 那天刷GitHub Trending,看到 google/ax 项目单日涨了1379颗星,我下意识划走——又一个带“Agent”字眼的Demo仓库吧?结果点进去扫了三行RE…

2026/10/22 14:11:52

珠宝AI设计新手实操指南:从提示词到工程落地全流程

1. 珠宝AI设计到底是做什么的 这两年"珠宝AI设计"在行业里被问得越来越多,我身边做珠宝定制、电商选品、甚至刚入行的设计师朋友,都在拿AI工具出概念图。先把这个事情说清楚:珠宝AI设计,指的是用AI绘画模型(…

2026/10/22 14:11:52

Docker 部署 Checkmate 监控避坑指南:compose 编排与监控项配置

docker 部署 Checkmate 监控,听起来就是一条命令拉镜像的事,但真到了生产环境,你依然会踩到端口冲突、数据库初始化失败、数据卷挂错路径这些坑。我这次把整个过程完整走了一遍,从选型、compose 编写到监控项配置,整理…

2026/10/22 14:06:49

AArch64平台Boost 1.69预编译包使用与交叉编译避坑指南

简介:这是一份针对aarch64(ARM 64位)平台交叉编译好的Boost 1.69.0库压缩包,该架构常见于高性能服务器、边缘计算盒、智能手机与平板等环境,面向在非aarch64主机上开发并部署到目标板的C工程师,解决Boost库…

2026/10/22 11:29:28

金融AI落地指南:从调研报告到技术选型与工程实践

简介:这份PDF报告聚焦2025年金融服务业中国AI现状与趋势,面向金融科技从业者、AI战略规划人员及关注行业数字化转型的研究者,帮助读者把握生成式AI在银行、支付、交易等场景中的落地路径与投资方向。资源包内含1个PDF文件,大小约1…

2026/10/22 0:01:49

电商智能运营体系从0到1:架构设计、落地步骤与避坑指南

前两年我们团队接了一个电商平台的运营中台项目,目标很直白:把过去靠运营拍脑袋做的选品、定价、发券、推活动这些事,逐步用数据算法和自动化流程代替。项目名字内部就叫“电商智能运营体系”。最近有不少朋友问我当时是怎么设计的、踩过哪些…

2026/10/22 0:01:49

若依框架AI代码生成不稳定?用Agent Skill三层结构+五步法搞定

最近一段时间,我一直想把后台管理系统里那些机械重复的增删改查代码交给AI去写。框架用的是若依,按理说这类业务代码模式非常固定,应该是AI最擅长的事情。可真试下来却不是那么回事——你让AI生成一个用户管理模块,它给出的Contro…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑