1.?线程a线̳߳? javaԴ??
2.Java线程池详解:Future的使用和实现
3.Java多线程——singleThreadExecutor
4.Java线程池实现原理及其在美团业务中的实践
5.Java高并发编程实战5,异步注解@Async自定义线程池
?池j程池̳߳? javaԴ??
在深入理解Java并发编程时,必不可少的源源码是对Semaphore源码的剖析。本文将带你探索这一核心组件,深度通过实践和源码解析,解析掌握其限流和共享锁的线程a线moxa源码本质。Semaphore,池j程池中文名信号量,源源码就像一个令牌桶,深度任务执行前需要获取令牌,解析处理完毕后归还,线程a线确保资源访问的池j程池有序进行。
首先,源源码Semaphore主要有acquire()和release()两个方法。深度acquire()负责获取许可,解析若许可不足,任务会被阻塞,直到有许可可用。release()用于释放并归还许可,确保资源释放后,其他任务可以继续执行。一个典型的例子是,如果一个线程池接受个任务,但Semaphore限制为3,那么任务将按每3个一组执行,确保系统稳定性。
Semaphore的源码实现巧妙地结合了AQS(AbstractQueuedSynchronizer)框架,通过Sync同步变量管理许可数量,公平锁和非公平锁的实现方式有所不同。公平锁会优先处理队列中的任务,而非公平锁则按照获取许可的顺序进行。
acquire()方法主要调用AQS中的acquireSharedInterruptibly(),并进一步通过tryReleaseShared()进行许可更新,公平锁与非公平锁的区别在于判断队列中是否有前置节点。release()方法则调用releaseShared(),更新许可数量。
Semaphore的bootdo项目源码分析简洁逻辑在于,AQS框架负责大部分并发控制,子类只需实现tryReleaseShared()和tryAcquireShared(),专注于许可数量的管理。欲了解AQS的详细流程,可参考之前的文章。
最后,了解了Semaphore后,我们还将继续探索共享锁CyclicBarrier的实现,敬请期待下篇文章。
Java线程池详解:Future的使用和实现
在处理异步任务时,Java线程池中的任务会返回一个Future对象,用于管理任务执行结果和状态。本文将详细介绍Future的使用和实现,包括获取执行结果、取消任务、获取任务状态以及FutureTask的详细实现。
1. 使用Future
1.1. 获取任务执行结果
Future提供了一个不带参数的get方法和一个带超时参数的get方法用于获取任务的执行结果。若任务执行完成,get方法会立即返回或抛出一个Exception;如果任务未完成,get方法会阻塞直到任务结束。带超时的get方法则会在指定时间内任务未完成时抛出TimeoutException。
java
ExecutorService es = Executors.newFixedThreadPool(1);
Future f = es.submit(new Callable() { @Override public Date call() throws Exception { Thread.sleep(); return new Date(); } });
try {
f.get(, TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {
System.out.println("timeout");
}
// 输出:timeout
如果任务抛出异常,get方法会将异常封装为ExecutionException并抛出,可以通过getCause方法获得原始异常。若任务被取消,get方法将抛出CancellationException。
1.2. 取消任务
通过Future可以取消任务,例如在get超时时抛弃任务时,应立即停止这些任务以避免为不再需要的结果浪费计算资源。
java
ExecutorService es = Executors.newFixedThreadPool(1);
Future f = es.submit(new Runnable() { @Override public void run() { throw new RuntimeException("task throw error test"); } });
// 等待任务执行完成
try {
f.get();
} catch (ExecutionException e) {
System.out.println(e.getCause().getMessage());
}
// 输出:task throw error test
1.3. 获取任务状态
Future提供了isDone和isCancelled两个方法来获取任务的状态。如果任务已完成,isDone返回true;如果任务被取消,isCancelled返回true。
java
/* 任务是否已完成。
完成可能是由于正常终止、异常或取消,市场发展指标源码在所有这些情况下,该方法将返回true。*/
boolean isDone();
/* Future是否已经被取消
在调用cancel后,无论cancel是否返回true改返回都将始终返回true。*/
boolean isCancelled();
2. FutureTask
FutureTask是Future的一个具体实现,实现了RunnableFuture接口。Executor框架下的线程池通常使用FutureTask作为底层实现。在AbstractExecutorService中,所有提交的任务都会被先封装为FutureTask,然后在execute方法中执行。FutureTask通过newTaskFor方法统一生成,并在任务执行前将其封装。
2.1. FutureTask的状态
FutureTask中的state字段记录了任务的当前状态,FutureTask中的操作在发起前都要先校验状态,操作后又要更新状态。FutureTask中的状态包括NEW、COMPLETING、NORMAL、EXCEPTIONAL、CANCELLED和INTERRUPTING,分别表示任务执行结束、执行抛出异常、被取消和被中断。
状态更新流程如下:通常调用get方法获取任务结果的线程与执行任务线程并非同一线程,因此为保证state字段的线程安全性,state字段的更新使用了Unsafe类的compareAndSwapObject方法,使用CAS算法进行更新。
2.2. get方法的实现
当调用FutureTask的get方法时,会阻塞直到任务执行完成或等待超时。其实现为在get方法中通过一个for(;;)循环一直循环到任务结束或超时,循环中执行:
- 当任务正常或异常执行完成后,会唤醒阻塞队列中的所有线程。
- 线程被唤醒后会返回任务的状态。
- 若调用get的线程被中断,则会立即抛出InterruptedException。
- 若任务被取消,油猴下载源码则会抛出CancellationException。
2.3. FutureTask的执行
生成FutureTask时,需要指定异步任务,如指定的任务类型为Runnable类型的,则会被转换为Callable类型并将任务保存在callable字段中。通常,FutureTask的任务会在一个异步线程中执行,即在异步线程中执行其run方法。而FutureTask的run方法会调用其持有的异步任务的call方法,获取call的执行结果来更新FutureTask的结果和状态。
初始时任务的状态为NEW和执行任务的线程(runner字段保存了执行任务的线程)为null,因此若run开始时非NEW状态或runner非空,则任务已被执行或正在执行中。为避免重复发起执行,这里会直接返回。call正常执行结束后或抛出异常结束时都会使用返回的结果或异常去更新状态。
若执行正常完成,则将outcome字段设置为执行返回的结果,FutureTask状态最终更新为正常结束NORMAL。若执行时抛出异常结束,则将outcome设置为抛出的异常,FutureTask状态最终更新为非正常结束EXCEPTIONAL。
任务结果更新完成后会通知到阻塞等待结果的线程。任务执行结束后遍历等待队列中的所有节点,将节点的被阻塞等待的线程逐个唤醒,并移除节点。这些操作由finishCompletion实现。
2.4. cancel方法的实现
cancel方法可以取消任务。取消时,若选择了中断,则先更新状态为INTERRUPTING,然后对执行任务的线程发出中断,之后再将任务状态更新为INTERRUPTED。在中断和状态更新完成后,表示任务已经被取消,因此最后也需要调用finishCompletion唤醒所有等待该任务执行结束的旅游源码小程序线程。
以上内容详细介绍了Future和FutureTask的使用与实现,帮助开发者更好地理解和运用异步任务处理机制。通过这些机制,开发者可以更高效地管理任务执行,处理结果获取和取消需求。
Java多线程——singleThreadExecutor
singleThreadExecutor,Java中Executors类的一个静态方法,创建了一个线程池,该线程池仅包含一个核心线程。这意味着所有任务将由这一单一线程执行,形成单线程执行模式。若核心线程因异常停止,则将启动新的线程替代,确保服务不中断。此线程池特别设计确保任务执行顺序与提交顺序一致,提升程序执行流程的可预测性与稳定性。
创建singleThreadExecutor的代码示例如下:
在这个例子中,ThreadPoolExecutor的corePoolSize和maximumPoolSize的值均为1,明确指出线程池仅包含一个核心线程,且最大线程数同样为1,保证了线程的高效利用。缓冲队列采用的是LinkedBlockingQueue,这是一个无边界队列,用于存储等待执行的任务。
Java线程池实现原理及其在美团业务中的实践
随着计算机行业的飞速发展,摩尔定律逐渐失效,多核CPU成为主流。使用多线程并行计算逐渐成为开发人员提升服务器性能的基本武器。J.U.C提供的线程池ThreadPoolExecutor类,帮助开发人员管理线程并方便地执行并行任务。了解并合理使用线程池,是一个开发人员必修的基本功。本文开篇简述了线程池概念和用途,接着结合线程池的源码,帮助大家领略线程池的设计思路,最后回归实践,通过案例讲述使用线程池遇到的问题,并给出了一种动态化线程池解决方案。一、写在前面
1.1 线程池是什么
线程池(Thread Pool)是一种基于池化思想管理线程的工具,经常出现在多线程服务器中,如MySQL。线程过多会带来额外的开销,包括创建销毁线程的开销、调度线程的开销等,同时也降低了计算机的整体性能。线程池维护多个线程,等待监督管理者分配可并发执行的任务。这种做法一方面避免了处理任务时创建销毁线程开销的代价,另一方面避免了线程数量膨胀导致的过分调度问题,保证了对内核的充分利用。本文描述的线程池是JDK中提供的ThreadPoolExecutor类。
1.2 线程池解决的问题是什么
线程池解决的核心问题就是资源管理问题。在并发环境下,系统不能确定在任意时刻有多少任务需要执行,有多少资源需要投入。这种不确定性将带来以下问题:资源分配问题、线程调度问题等。线程池采用了“池化”思想来解决这些问题。Pooling是将资源统一管理的一种思想,不仅能应用在计算机领域,还在金融、设备、人员管理、工作管理等领域有相关应用。在计算机领域,表现为统一管理IT资源,包括服务器、存储、网络等,通过共享资源在低投入中获益。
二、线程池核心设计与实现
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方法完成检查线程池状态、运行线程数和运行策略,决定执行流程,如直接申请线程执行或缓冲到队列执行,或直接拒绝任务。执行流程如下。
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高并发编程实战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不断的切换,对整个系统性能也不会有太大的提升,反而会导致系统缓慢。
六、线程池执行流程