Java内功修炼(3)——并发的四重境界:单例之固、生产消费之衡、定时之准、池化之效

Java内功修炼(3)——并发的四重境界:单例之固、生产消费之衡、定时之准、池化之效

1.单例模式

1.1 概述

单例模式(Singleton Pattern):是一种常用的设计模式,主要用于确保一个类在整个应用程序中只有一个实例,并提供一个全局访问点核心作用1.控制资源访问:常用于管理共享资源,可以避免多线程竞争或重复创建资源导致的性能问题2.全局状态管理:当某些对象需要被多个模块或组件共享时,单例模式提供统一的访问入口3.保证数据一致性:避免多个实例导致的数据不一致问题常见实现方式饿汉模式:在类加载时就创建实例,避免了线程安全问题,但可能会造成资源浪费,尤其是当实例初始化过程复杂或占用较多资源时懒汉模式:一种延迟初始化的单例实现方式。实例在第一次被使用时才创建,而非在类加载时就创建。这种方式可以节省资源,但需要考虑线程安全问题

1.2 饿汉模式

publicclassSingleton{//类加载时进行实例化privatestaticfinalSingleton hungry =newSingleton();//全局唯一获取实例的接口publicstaticSingletongetInstance(){return hungry;}//构造方法私有化privateSingleton(){}}
特点:类加载时进行实例化不存在运行时实例化过程,所以不存在线程安全问题

缺点:即使后面的场景中没有使用到该实例,也会将该实例创建出来,可能会造成不必要的资源浪费

1.3 懒汉模式

classSingleton{//volatile:禁止指令重排序privatestaticvolatileSingleton lazy =null;//创建锁对象privatestaticfinalObject object =newObject();//全局唯一获取实例的接口publicstaticSingletongetInstance(){//外层if判断:优化,提高性能if(lazy ==null){//避免多线程时实例化多个对象synchronized(object){if(lazy ==null){ lazy =newSingleton();}}}return lazy;}//构造方法私有化privateSingleton(){}}
实现细节:1.通过synchronized加锁解决线程安全问题2.外层if判断,减少锁的开销3.volatile防止指令重排序(避免半初始化对象)

特点:只有在真正需要时才创建实例,减少系统启动时的资源占用,资源利用率高

缺点:若未正确使用同步机制(如synchronized或volatile),可能导致多线程环境下实例被多次创建,线程安全实现复杂

1.4 懒汉模式半初始化

Java对象初始化流程1.内存分配阶段:当使用new关键字创建对象时,JVM会在堆内存中为该对象分配空间2.对象初始化阶段:对象内存分配完成后,开始执行初始化3.引用赋值阶段:内存分配完成后,JVM将分配的内存地址赋值给引用变量Java对象初始化流程(指令重排序后)1.内存分配阶段:当使用new关键字创建对象时,JVM会在堆内存中为该对象分配空间2.引用赋值阶段:内存分配完成后,JVM将分配的内存地址赋值给引用变量3.对象初始化阶段:对象内存分配完成后,开始执行初始化

1.5 懒汉/饿汉优缺点对比

特性懒汉模式饿汉模式
实例化时机第一次使用时类加载时
资源消耗节省资源可能浪费资源
线程安全需要额外同步机制天然线程安全
实现复杂度较复杂简单
适用场景实例化开销大,延迟加载实例化开销小,不需要延迟加载

2.生产者/消费者模式

2.1 概述

生产者/消费者模式(Producer/consumer model):用于协调多个线程或进程之间的任务分配与数据处理。生产者负责生成数据或任务,消费者负责处理这些数据或任务,二者通过共享的缓冲区(队列)进行解耦,避免直接依赖核心作用1.解耦生产与消费逻辑:生产者仅负责生成数据并放入缓冲区,消费者仅从缓冲区获取数据并处理。两者无需直接交互,降低代码复杂度,提高模块化程度2.平衡处理速率差异:生产者与消费者通常以不同速度运行。缓冲区作为中间层,允许生产者持续写入数据,消费者按自身能力消费,避免互相阻塞3.削峰填谷:通过缓冲队列平滑流量波动,避免系统因瞬时高负载崩溃。当生产者突然产生大量请求时,缓冲区暂时存储这些请求,消费者按照自身处理能力逐步消费;当生产者速度降低时,缓冲区逐步释放积压的请求,保持消费者稳定工作

2.2 实现阻塞队列

classMyBlockingQueue{privateint head =0;privateint tail =0;privateint useSize =0;privatefinalString[] array;publicMyBlockingQueue(int capacity){ array =newString[capacity];}//添加publicsynchronizedvoidput(String string)throwsInterruptedException{if(isFull()){//队列满了,等待消费者消耗元素this.wait();} array[tail]= string; tail++; tail =(tail +1)% array.length; useSize++;this.notify();}//删除publicStringtake()throwsInterruptedException{String ret;synchronized(this){if(useSize <=0){//队列空了,等待生产者添加元素.this.wait();} ret = array[head]; head++; head =(head +1)% array.length; useSize--;this.notify();}return ret;}//判断是否满了publicbooleanisFull(){return useSize >= array.length;}}

2.3 实现生产者/消费者模式

publicclassProducer_Consumer_Blog{publicstaticvoidmain(String[] args){MyBlockingQueue queue =newMyBlockingQueue(1000);Thread thread1 =newThread(()->{int n =1;while(true){try{ queue.put(n +"");System.out.println("生产元素n = "+ n); n++;}catch(InterruptedException e){thrownewRuntimeException(e);}}});Thread thread2 =newThread(()->{while(true){try{System.out.println("消费元素n = "+ queue.take());Thread.sleep(1000);}catch(InterruptedException e){thrownewRuntimeException(e);}}}); thread1.start(); thread2.start();}}

3.定时器

3.1 概述

定时器(Timer):用于在特定时间间隔或指定时间点执行任务的编程模式,广泛应用于定时任务调度、延迟操作、周期性任务等场景。核心思想是将任务的执行逻辑与时间控制解耦,通过统一的定时器管理多个任务核心作用/特点1.管理异步任务调度:Timer允许你安排一个任务在未来的某个时间点执行,或者以固定的间隔重复执行2.后台执行:Timer可以使用一个后台线程来执行任务,这意味着调度和执行任务不会阻塞主线程(主线程结束后后台线程跟着结束)3.简单易用:Timer提供了一个相对简单的方式来处理定时任务,适合用于不需要复杂调度的场景

标准库Timer构造方法

//1.默认构造方法//创建一个Timer对象,是一个后台线程,并使用线程的默认名字publicTimer(){this("Timer-"+serialNumber());}//2.指定线程名字的构造方法//创建一个Timer对象,是一个后台线程,并使用指定的线程名字publicTimer(String name){ thread.setName(name); thread.start();}//3.指定是否为后台线程的构造方法//传入true,是后台线程;传入false,是前台线程publicTimer(boolean isDaemon){this("Timer-"+serialNumber(), isDaemon);}//4.指定线程名字和是否为后台线程的构造方法publicTimer(String name,boolean isDaemon){ thread.setName(name); thread.setDaemon(isDaemon); thread.start();}

标准库Timer的schedule方法

  • 1.schedule(TimerTask task, Date time):安排任务在指定的时间执行一次
publicstaticvoidmain(String[] args){Timer timer =newTimer();TimerTask timerTask =newTimerTask(){@Overridepublicvoidrun(){System.out.println("延迟三秒执行");}};//使用Date对象来指定具体的执行时间//new Date(System.currentTimeMillis()+1000表示当前时间等待1000ms timer.schedule(timerTask,newDate(System.currentTimeMillis()+1000));}
  • 2.schedule(TimerTask task, Date firstTime, long period):安排任务在指定的时间首次执行,然后每隔一段时间重复执行
publicstaticvoidmain(String[] args){Timer timer =newTimer();TimerTask timerTask =newTimerTask(){@Overridepublicvoidrun(){System.out.println("延迟三秒执行");}};//当前时间等待1000ms后第一次执行任务//此后每间隔1000ms就执行一次任务 timer.schedule(timerTask,newDate(System.currentTimeMillis()+1000),1000);}
  • 3.schedule(TimerTask task, long delay):安排任务在指定的延迟时间后执行一次(相对于当前时间)
publicstaticvoidmain(String[] args){Timer timer =newTimer();TimerTask timerTask =newTimerTask(){@Overridepublicvoidrun(){System.out.println("延迟三秒执行");}};//当前时间延迟3000ms后执行 timer.schedule(timerTask,3000);}
  • 4.schedule(TimerTask task, long delay, long period):安排任务在指定的延迟时间后首次执行,然后每隔一段时间重复执行
publicstaticvoidmain(String[] args){Timer timer =newTimer();TimerTask timerTask =newTimerTask(){@Overridepublicvoidrun(){System.out.println("延迟三秒执行");}};//当前时间延迟3000ms后执行//此后每间隔3000ms就执行一次任务 timer.schedule(timerTask,3000,3000);}

3.2模拟实现定时器

classMyTaskimplementsComparable<MyTask>{privatefinalRunnable runnable;privatefinallong time;publicMyTask(Runnable runnable,long delay){this.runnable = runnable;this.time =System.currentTimeMillis()+ delay;}publiclonggetTime(){returnthis.time;}publicvoidrun(){ runnable.run();}@OverridepublicintcompareTo(MyTask o){return(int)(this.time - o.time);}}classMyTime{privatefinalPriorityQueue<MyTask> queue =newPriorityQueue<>();publicvoidschedule(Runnable runnable,long delay){synchronized(this){MyTask myTask =newMyTask(runnable, delay); queue.offer(myTask);this.notify();}}publicMyTime(){Thread thread =newThread(()->{while(true){try{synchronized(this){while(queue.isEmpty()){this.wait();}MyTask myTask = queue.peek();long curTime =System.currentTimeMillis();if(curTime >= myTask.getTime()){ myTask.run(); queue.poll();}else{this.wait(myTask.getTime()- curTime);}}}catch(InterruptedException e){thrownewRuntimeException(e);}}}); thread.setDaemon(true); thread.start();}}

4.线程池

4.1 概述

线程池:线程池是一种管理和复用线程的编程模式。它预先创建一定数量的线程,在执行任务需要时,将任务分配给这些线程,从而提高运行效率核心作用:优化多线程任务的执行效率与管理资源特点线程复用:当线程执行完一个任务时,不会立即销毁,而是等待下一个任务的到来(当然这种等待是有时间限制的),这样避免了频繁的创建和销毁线程动态调整:根据实际环境需要动态调整线程数量,以达到最佳性能任务队列:线程池会维护一个任务队列,用于存放待执行的任务,当线程空闲时,从队列中取出任务并执行标准库线程池构造方法1.int corePoolSize:核心线程数2.int maximumPoolSize:最大线程数3.long keepAliveTime:非核心线程的空闲时的最大存活时间4.TimeUnit unit:时间单位5.BlockingQueue< Runnable > workQueue:任务队列6.ThreadFactory threadFactory:线程工厂,用于创建新线程的工厂7.RejectedExecutionHandler handler:拒绝策略

4.3线程池的执行流程

假设现在有一个线程池:核心线程数2,最大线程数4,等待队列2任务数量<=2(A,B)时,由核心线程执行任务2<任务数量<=4(A,B,C,D)时,核心线程无法同时处理所有任务,未被执行的任务(C,D)将会进入等待队列中等待核心线程执行4<任务数量<=6(A,B,C,D,E,F),此时等待队列也满了,线程池就会就会开放非核心线程来执行任务,C和D任务继续在等待队列中等待,新添加的E和F任务由非核心线程来执行任务数量>6,核心线程,等待队列,非核心线程都被任务所占用,仍然无法满足需求,此时就会触发线程池的拒绝策略

4.4 拒绝策略

4.DiscardOldestPolicy:丢弃最老的任务

在这里插入图片描述

3.DiscardPolicy:丢弃新任务

在这里插入图片描述

2.CallerRunsPolicy:由提交该任务的线程来执行

在这里插入图片描述

1.AbortPolicy:直接抛异常

在这里插入图片描述

4.5 模拟实现线程池

publicclassMyThreadPoolExecutor{privatefinalint capacity =1000;//阻塞队列privatefinalMyBlockingQueue queue =newMyBlockingQueue(capacity);privatefinalList<Thread> list =newArrayList<>();//创建线程publicMyThreadPoolExecutor(int n){for(int i =0; i < n; i++){Thread thread =newThread(()->{while(true){try{ queue.take().run();}catch(InterruptedException e){thrownewRuntimeException(e);}}}); thread.start(); list.add(thread);}}//添加任务publicvoidsubmit(Runnable runnable)throwsInterruptedException{ queue.put(runnable);}publicintgetCapacity(){return capacity;}//获取线程publicList<Thread>getList(){return list;}}

Read more

Python 多线程日志错乱:logging.Handler 的并发问题

Python 多线程日志错乱:logging.Handler 的并发问题

Python 多线程日志错乱:logging.Handler 的并发问题 🌟 Hello,我是摘星! 🌈 在彩虹般绚烂的技术栈中,我是那个永不停歇的色彩收集者。 🦋 每一个优化都是我培育的花朵,每一个特性都是我放飞的蝴蝶。 🔬 每一次代码审查都是我的显微镜观察,每一次重构都是我的化学实验。 🎵 在编程的交响乐中,我既是指挥家也是演奏者。让我们一起,在技术的音乐厅里,奏响属于程序员的华美乐章。 目录 Python 多线程日志错乱:logging.Handler 的并发问题 摘要 1. 问题现象与复现 1.1 典型的日志错乱场景 2. logging模块的线程安全机制分析 2.1 Handler级别的线程安全 2.2 锁竞争的性能影响分析 3. 深入源码:竞态条件的根本原因 3.1 Handler.emit()方法的竞态分析 3.2 I/O操作的原子性问题

By Ne0inhk

从Python到AI的完整成长路径:新手少走90%弯路,一步步落地不迷茫

很多人想学AI,却总卡在第一步——不知道Python学到什么程度才算够,也不清楚从代码到AI模型到底要跨过哪些坎,要么盲目啃理论,要么瞎找项目练手,最后半途而废。 作为从零基础Python入门,一步步走到AI实战、整理过机器学习项目合集的过来人,我把从Python到AI的完整过程拆成4个核心阶段,没有晦涩术语,每一步都有明确目标和实操方向,新手照着走,就能平稳过渡,不迷茫、不踩坑。 第一阶段:Python打底——不是全学,而是学“AI刚需部分” 很多人误区:把Python从基础语法到爬虫、Web开发全学一遍,浪费半年时间,其实AI方向不需要全学Python,只需要掌握核心工具包和基础语法,够写AI相关代码就行,1-2个月就能搞定。 必学内容(优先级从高到低): 1. 基础语法(快速过):变量、数据类型、循环、条件判断、函数、类与对象,不用深究复杂语法,能看懂代码、写简单逻辑即可; 2. AI三大核心库(重中之重): 1. NumPy:数组运算、

By Ne0inhk
【测试基础】Python 核心语法,一篇搞定测试脚本开发基础

【测试基础】Python 核心语法,一篇搞定测试脚本开发基础

🔥个人主页: 中草药  🔥专栏:【Java】登神长阶 史诗般的Java成神之路 本文不做Python以及Pycharm安装的详细教程,请大家自行查阅资料,或到官网去下载         Python作为一门 “优雅且强大” 的编程语言,Python 凭借易上手、用途广的特点,成为很多人入门编程的首选。无论是数据分析、人工智能,还是 Web 开发、自动化脚本,Python 都能胜任。但想要用好 Python,扎实的基础语法是关键 —— 本文将结合系统的语法知识,从 “计算器” 级别的简单运算,到数据持久化的文件操作,带你一站式吃透 Python 核心语法,让你看完就能上手写代码! 变量与数据类型:程序的"原材料仓库"         变量就像快递盒,用来装不同类型的数据;数据类型则是快递盒上的标签,告诉我们里面装的是文件、水果还是电子产品。类型系统其实是在对变量进行 "归类"

By Ne0inhk
C++/数据结构:哈希表知识点

C++/数据结构:哈希表知识点

目录 哈希表 理解哈希表 哈希值(整形) BKDR哈希   异或组合  hash_combine 哈希函数 直接定址法 除留余数法 平方取中法 基数转换法 哈希冲突 开放定址法 哈希桶 unordered_map和unorder_set如何共用一个哈希桶模板类 stl的哈希桶中Insert如何得到的键值 键为自定义类型的处理         前言:本篇文章前半部分内容为哈希表的原理, 从上到下按照理解链逐层递进。 最后三个小标题占了比较大的篇幅, 是结合c++代码来叙述, 主要内容为stl中的哈希桶如何封装的。 如果有错误的地方, 欢迎友友们指正哦。         ps:本篇文章一直到哈希桶,除了最后三个小标题,c++和java的同学都可以看, 讲的是数据结构, 即便有c++代码也很简单哦。 哈希表         首先要理解哈希和哈希表有什么不同。 哈希就是映射, 是一种算法思想。 哈希表就是映射表, 是利用映射这种思想写出的一种数据结构。          所有的哈希表的算法流程都是类似的——拿到一个key, 利用哈希函数进行hash

By Ne0inhk