
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列管理线程到了时间阻塞 当队列为空 也阻塞调用工作线程 队列空阻塞比较方法由阻塞队列调用另一种模拟“时间轮”利用循环队列 每个元素是“时间单位” 每一个元素是链表 每到一个时间单位 遍历链表 对应的任务性能高不如优先级队列精度好