1.Java 中的线程a线 4 种线程池
2.Java多线程——singleThreadExecutor
3.Java高并发编程实战5,异步注解@Async自定义线程池
4.Java线程池实现原理及其在美团业务中的池源程池实践
5.Java并发必会,深入剖析Semaphore源码
6.Java的源码并行世界-3.0 线程池与拒绝策略
Java 中的 4 种线程池
Java中的线程池,其核心接口为ExecutorService,解析而非Executor。线程a线Executor负责执行线程,池源程池表白源码搭建而实际线程池接口为ExecutorService。源码其设计旨在高效处理大量短期异步任务,解析提高程序性能。线程a线
创建一个可重用、池源程池固定线程数的源码线程池,共享无界队列方式运作。解析此池在大多数nThreads状态活动,线程a线提交额外任务需等待,池源程池直至可用线程出现。源码关闭前,任务始终执行。新线程在任何因失败导致的终止情况后补位。
另一个实例是可安排在指定延迟后执行或定期执行的任务线程池。这为复杂调度提供强大支持,简化定时任务管理。
使用Executors.newSingleThreadExecutor()构建线程池,实现单线程循环功能。单线程池在线程死亡或发生异常后能重新启动,持续执行任务。
Java多线程——singleThreadExecutor
singleThreadExecutor,Java中Executors类的一个静态方法,创建了一个线程池,该线程池仅包含一个核心线程。这意味着所有任务将由这一单一线程执行,形成单线程执行模式。若核心线程因异常停止,则将启动新的线程替代,确保服务不中断。此线程池特别设计确保任务执行顺序与提交顺序一致,提升程序执行流程的可预测性与稳定性。
创建singleThreadExecutor的代码示例如下:
在这个例子中,ThreadPoolExecutor的corePoolSize和maximumPoolSize的值均为1,明确指出线程池仅包含一个核心线程,且最大线程数同样为1,保证了线程的高效利用。缓冲队列采用的是LinkedBlockingQueue,这是一个无边界队列,用于存储等待执行的大乐透源码任务。
Java高并发编程实战5,异步注解@Async自定义线程池
@Async注解的作用是异步处理任务。
在使用@Async时,如果不指定线程池的名称,默认线程池是Spring默认的线程池SimpleAsyncTaskExecutor。
默认线程池的配置如下:
从最大线程数可以看出,在并发情况下,会无限制地创建线程。
也可以通过yml重新配置:
也可以自定义线程池,下面通过简单的代码来实现@Async自定义线程池。
二、代码实例
导入POM
配置类AsyncTaskConfig
UserController
UserService
UserServiceImpl
三、为什么在文件内执行异步任务,还是一个线程,没有实现@Async效果?
在众多尝试中,找到了@Async失效的几个原因:
四、配置中使用了ThreadPoolTaskExecutor和ThreadPoolExecutor,这两个有什么区别?
ThreadPoolTaskExecutor是spring core包中的,而ThreadPoolExecutor是JDK中的JUC。
1、initialize()
查看ThreadPoolTaskExecutor的initialize()方法
2、initializeExecutor抽象方法
再查看initializeExecutor抽象方法的具体实现类,其中有一个就是ThreadPoolTaskExecutor类,查看它的initializeExecutor方法,使用的就是ThreadPoolExecutor。
因此可以了解到ThreadPoolTaskExecutor是对ThreadPoolExecutor进行了封装。
五、核心线程数
配置文件中的线程池核心线程数为何配置为Runtime.getRuntime().availableProcessors()?
获取的是CPU核心线程数,也就是计算资源。
在实际中,需要对具体的线程池大小进行调整,可以通过压测及机器设备现状,进行调整大小。如果线程池太大,则会造成CPU不断的切换,对整个系统性能也不会有太大的提升,反而会导致系统缓慢。
六、线程池执行流程
Java线程池实现原理及其在美团业务中的实践
随着计算机行业的飞速发展,摩尔定律逐渐失效,多核CPU成为主流。使用多线程并行计算逐渐成为开发人员提升服务器性能的基本武器。J.U.C提供的chart源码线程池ThreadPoolExecutor类,帮助开发人员管理线程并方便地执行并行任务。了解并合理使用线程池,是一个开发人员必修的基本功。本文开篇简述了线程池概念和用途,接着结合线程池的源码,帮助大家领略线程池的设计思路,最后回归实践,通过案例讲述使用线程池遇到的问题,并给出了一种动态化线程池解决方案。一、写在前面
1.1 线程池是什么
线程池(Thread Pool)是一种基于池化思想管理线程的工具,经常出现在多线程服务器中,如MySQL。线程过多会带来额外的开销,包括创建销毁线程的开销、调度线程的开销等,同时也降低了计算机的整体性能。线程池维护多个线程,等待监督管理者分配可并发执行的任务。这种做法一方面避免了处理任务时创建销毁线程开销的代价,另一方面避免了线程数量膨胀导致的过分调度问题,保证了对内核的充分利用。本文描述的线程池是JDK中提供的ThreadPoolExecutor类。
1.2 线程池解决的问题是什么
线程池解决的核心问题就是资源管理问题。在并发环境下,系统不能确定在任意时刻有多少任务需要执行,有多少资源需要投入。这种不确定性将带来以下问题:资源分配问题、线程调度问题等。线程池采用了“池化”思想来解决这些问题。Pooling是将资源统一管理的一种思想,不仅能应用在计算机领域,还在金融、设备、人员管理、工作管理等领域有相关应用。在计算机领域,表现为统一管理IT资源,包括服务器、存储、网络等,通过共享资源在低投入中获益。
二、27270源码线程池核心设计与实现
Java中的线程池核心实现类是ThreadPoolExecutor,本文基于JDK 1.8的源码来分析线程池的核心设计与实现。首先,我们通过ThreadPoolExecutor的UML类图了解其继承关系,然后深入探讨其设计与实现。
2.1 总体设计
ThreadPoolExecutor实现的顶层接口是Executor,提供了一种思想:将任务提交和任务执行进行解耦。用户只需提供Runnable对象,将任务的运行逻辑提交到执行器(Executor)中,由Executor框架完成线程的调配和任务的执行。ExecutorService接口增加了能力,如补充可以为一个或一批异步任务生成Future的方法以及提供管控线程池的方法,如停止线程池运行。
AbstractExecutorService是上层的抽象类,将执行任务的流程串联起来,保证下层实现只需关注执行任务的方法。ThreadPoolExecutor作为最下层的实现类,实现最复杂的运行部分,负责维护自身的生命周期和管理线程与任务,使两者结合执行并行任务。
ThreadPoolExecutor运行机制分为任务管理和线程管理两部分。任务管理充当生产者的角色,线程池会根据任务的流转决定执行流程。线程管理是消费者,维护线程池内的线程,根据任务请求进行线程分配。
2.2 生命周期管理
线程池运行状态由内部维护,使用变量控制线程池的运行状态和有效线程数量。线程池内部使用AtomicInteger存储关键参数,实现线程池运行状态和线程数量的高效管理。线程池提供方法供用户获取当前运行状态和线程数量,通过位运算实现快速计算。
ThreadPoolExecutor的运行状态有五种,包含生命周期转换。
2.3 任务执行机制
2.3.1 任务调度
任务调度是线程池核心入口,用户提交任务后,决定任务执行流程。通过execute方法完成检查线程池状态、运行线程数和运行策略,决定执行流程,如直接申请线程执行或缓冲到队列执行,或直接拒绝任务。执行流程如下。minertool源码
2.3.2 任务缓冲
任务缓冲模块实现任务和线程的管理,通过生产者消费者模式和阻塞队列实现。阻塞队列缓存任务,工作线程从队列中获取任务。
2.3.3 任务申请
任务执行有两种可能:直接由新创建的线程执行或从队列中获取任务执行。线程从任务缓存模块不断获取任务,通过getTask方法实现线程管理和任务管理之间的通信。
2.3.4 任务拒绝
任务拒绝策略保护线程池,实现拒绝策略接口定制策略或选择JDK提供的四种已有策略。拒绝策略特点如下。
2.4 Worker线程管理
2.4.1 Worker线程
Worker线程实现Runnable接口,持有线程和任务,通过构造方法创建。Worker线程执行任务模型如下,线程池通过AQS实现独占锁,控制线程生命周期,回收线程。
2.4.2 Worker线程增加
Worker线程增加通过addWorker方法实现,增加线程时考虑线程池状态,策略在上一步完成,仅完成增加线程并运行,最后返回成功结果。方法参数包括firstTask和core,用于指定任务和线程策略。
2.4.3 Worker线程回收
Worker线程回收依赖JVM自动回收,线程池维护线程引用,通过添加和移除引用控制线程生命周期。Worker被创建后,不断获取任务执行,核心线程无限等待,非核心线程限时获取。当无法获取任务时,循环结束,Worker主动移除自身引用。
2.4.4 Worker线程执行任务
Worker线程执行任务通过runWorker方法实现,执行流程如下。
三、线程池在业务中的实践
业务实践中,线程池用于获取并发性,提供典型场景和问题解决方案。
3.1 业务背景
互联网业界追求CPU多核性能,通过线程池管理线程获取并发性。常见场景包括快速响应用户请求和快速处理批量任务。
3.2 实际问题及方案思考
线程池使用面临核心问题:参数配置困难。调研替代方案、参数设置合理性以及线程池参数动态化,动态化线程池提供简单有效的方法解决参数修改成本问题。
3.3 动态化线程池
动态化线程池设计包括整体设计、功能架构,提供参数动态化、监控和告警能力。动态化线程池允许用户在管理平台上修改参数,实时生效,并监控线程池负载、任务执行情况,提供任务级别监控和运行时状态查看。
3.4 实践总结
面对使用线程池的实际问题,动态化线程池提供成本效益平衡的解决方案,降低故障发生的概率,适用于业务需求。
四、参考资料
1. JDK 1.8 源码
2. 维基百科-线程池
3. 更好的使用Java线程池
4. 维基百科Pooling(Resource Management)
5. 深入理解Java线程池:ThreadPoolExecutor
6. 《Java并发编程实践》
Java并发必会,深入剖析Semaphore源码
在深入理解Java并发编程时,必不可少的是对Semaphore源码的剖析。本文将带你探索这一核心组件,通过实践和源码解析,掌握其限流和共享锁的本质。Semaphore,中文名信号量,就像一个令牌桶,任务执行前需要获取令牌,处理完毕后归还,确保资源访问的有序进行。
首先,Semaphore主要有acquire()和release()两个方法。acquire()负责获取许可,若许可不足,任务会被阻塞,直到有许可可用。release()用于释放并归还许可,确保资源释放后,其他任务可以继续执行。一个典型的例子是,如果一个线程池接受个任务,但Semaphore限制为3,那么任务将按每3个一组执行,确保系统稳定性。
Semaphore的源码实现巧妙地结合了AQS(AbstractQueuedSynchronizer)框架,通过Sync同步变量管理许可数量,公平锁和非公平锁的实现方式有所不同。公平锁会优先处理队列中的任务,而非公平锁则按照获取许可的顺序进行。
acquire()方法主要调用AQS中的acquireSharedInterruptibly(),并进一步通过tryReleaseShared()进行许可更新,公平锁与非公平锁的区别在于判断队列中是否有前置节点。release()方法则调用releaseShared(),更新许可数量。
Semaphore的简洁逻辑在于,AQS框架负责大部分并发控制,子类只需实现tryReleaseShared()和tryAcquireShared(),专注于许可数量的管理。欲了解AQS的详细流程,可参考之前的文章。
最后,了解了Semaphore后,我们还将继续探索共享锁CyclicBarrier的实现,敬请期待下篇文章。
Java的并行世界-3.0 线程池与拒绝策略
Jdk并发相关核心类:java.util.concurrent
java.util.concurrent.Executors提供了一些静态工厂方法,用于创建不同类型的线程池,例如:
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool();
可以通过new ThreadPoolExecutor()方法手动创建线程池,该方法需要传入四个参数,分别是核心线程数、最大线程数、线程保活时间和任务队列。其中,核心线程数和最大线程数是必填参数,线程保活时间和任务队列是可选参数。
Java中的Executors共有四种创建方式,这些方式包括使用newFixedThreadPool、newCachedThreadPool、newSingleThreadExecutor和newScheduledThreadPool。在使用这些方法时,可以根据实际需求选择最适合的方式来创建线程池。无论哪种方式,线程池都可以有效地管理和控制线程,提高程序的执行效率。
新FixedThreadPool创建一个固定大小的线程池。
以下是一个Java中创建newFixedThreadPool的代码例子:
新CachedThreadPool创建一个根据需要自动扩展的线程池,线程数根据任务数量动态调整。
以下是一个newCachedThreadPool的Java代码示例:
新SingleThreadExecutor创建一个只有一个线程的线程池。
新ScheduledThreadPool创建一个支持定时任务的线程池。
ForkJoinPool是一个用于执行分而治之任务的线程池,特别适用于递归分解的问题,例如并行归并排序、并行求和等。
ForkJoinPool forkJoinPool = new ForkJoinPool();
ForkJoinPool在java.util.concurrent包下,从Java 7开始引入,专门用于处理需要递归分解的任务。它利用工作窃取(Work-Stealing)算法来实现高效的任务调度和线程利用,能够充分利用多核处理器的优势。
ForkJoinPool的主要特点包括:
下面是一个简单的使用ForkJoinPool的示例,计算斐波那契数列的值:
虽然上面5个线程池看上去功能特点不同,但是其内部实现原理都调用了JDK的ThreadPoolExecutor线程池类。
ThreadPoolExecutor的构造函数有多个参数,允许根据实际需求配置线程池的行为,主要包括:
corePoolSize:线程池的核心线程数,即线程池中保持的最小线程数量。即使线程处于空闲状态,核心线程也不会被销毁。
maximumPoolSize:线程池的最大线程数,即线程池中允许的最大线程数量。当任务数量超过核心线程数时,线程池会根据实际情况动态地创建新的线程,但不会超过最大线程数。
keepAliveTime:非核心线程的空闲时间超过这个时间后,会被销毁,从而控制线程池的大小。时间单位可以通过指定的TimeUnit来定义。
workQueue:任务队列,用于存放等待执行的任务。ThreadPoolExecutor支持多种类型的任务队列,如ArrayBlockingQueue、LinkedBlockingQueue等。
threadFactory:用于创建线程的工厂,可以自定义线程的创建方式。
handler:拒绝策略,当线程池和队列都满了,无法继续接受新的任务时,会触发拒绝策略来处理新的任务。常见的拒绝策略包括AbortPolicy(默认策略,直接抛出异常)、CallerRunsPolicy(由调用线程来执行被拒绝的任务)、DiscardPolicy(丢弃被拒绝的任务)、DiscardOldestPolicy(丢弃队列中最老的任务)。
ThreadPoolExecutor提供了submit()和execute()等方法来向线程池提交任务,其中:
submit()方法可以接受Callable和Runnable类型的任务,并返回一个Future对象,通过这个对象可以获取任务的执行结果或者取消任务。
execute()方法只能接受Runnable类型的任务,无法获取任务的返回结果。
ThreadPoolExecutor还提供了一些方法来管理和监控线程池的状态,如getActiveCount()、getCompletedTaskCount()、getTaskCount()等。
workQueue:任务队列,其中的任务是被提交但尚未执行的任务。其类型是:BlockingQueue接口,只能用于存放Runable对象。
有界队列可以通过ArrayBlockingQueue实现,当有新任务到来时,如果线程池的实际线程数量小于corePoolSize,则会优先创建新的线程;如果大于corePoolSize,则会将新任务加入等待队列;若队列已满,无法加入,则在总线程数量不大于maximumPoolSize的前提下,创建新的线程执行任务;若大于maximumPoolSize,则执行拒绝策略。
无界队列可以通过LinkedBlockingQueue实现,与有界队列相比,除非系统资源耗尽,不然当有新的任务到来,并且线程数量小于corePoolSize时,线程池就会创建新的线程执行任务。但当线程数量大于corePoolSize后,就不会继续创建了。若还有任务进来,系统CPU没那么忙,还有线程资源,则任务直接进入队列等待。
优先任务队列可以通过PriorityBlockingQueue实现,可以根据任务的优先级执行任务,这是一种特殊的无界队列。
有界队列和无界队列需要做demo测试。新CachedThreadPool内部使用的是SynchronousQueue队列,这是一个直接提交队列,系统会增加新的线程执行任务,当任务执行完毕后,线程会被回收;如果开启大量任务提交,每个任务执行慢,系统会开启等量的线程处理,直到系统资源耗尽。
ThreadPoolExecutor的核心调度代码包括workerCountOf(c)来获取当前工作线程池的总数,addWorker(command)用于提交任务或创建线程池,以及reject(command)来执行拒绝策略。
Handler是RejectedExecutionHandler接口类型,代表了不同的拒绝策略。常见的拒绝策略包括:
CallerRunsPolicy:如果线程池未关闭,会直接在调用者当前线程执行等待队列放不下的任务。
AbortPolicy:会直接抛出异常,阻止系统正常运行。
DiscardPolicy:直接丢失无法放入等待队列的任务,不做异常抛出。
DiscardOldestPolicy:丢弃最老的一个请求,然后尝试把当前请求任务加入到等待队列。
注意:在创建ThreadPoolExecutor时需要指定拒绝策略,如果以上拒绝策略无法满足,可以继承RejectedExecutionHandler接口来实现自定义的拒绝策略。
最常用的是升级DiscardPolicy策略,但需要在放弃前记录请求;示例如下:
以下是RejectedExecutionHandler接口代码,可以重新实现该方法以满足自定义需求。
熟悉拒绝策略后,在线程池中还有重要参数ThreadFactory,用于控制线程的创建。通过ThreadFactory,可以实现以下功能:
命名线程:通过为线程指定有意义的名称,便于跟踪日志和调试信息。
设置线程属性:根据需要设置线程的优先级、守护状态、异常处理器等。
定制化线程创建逻辑:添加自定义逻辑来创建线程,如记录线程的创建次数、设置线程组等。
以下是简单的ThreadFactory示例:
2024-12-27 12:21
2024-12-27 11:39
2024-12-27 11:34
2024-12-27 11:20
2024-12-27 10:57
2024-12-27 10:29