线程池是并发包里面很重要的一部分,在实际情况中也是使用很多的一个重要组件。
下图描述的是线程池API的一部分。广义上的完整线程池可能还包括Thread/Runnable、Timer/TimerTask等部分。这里只介绍主要的和高级的API以及架构和原理。
一、任务的执行策略
任务的执行策略包括4W3H部分:
- 任务在什么(What)线程中执行
- 任务以什么(What)顺序执行(FIFO/LIFO/优先级等)
- 同时有多少个(How Many)任务并发执行
- 允许有多少个(How Many)个任务进入执行队列
- 系统过载时选择放弃哪一个(Which)任务,如何(How)通知应用程序这个动作
- 任务执行的开始、结束应该做什么(What)处理
在后面的章节中会详细分写这些策略是如何实现的。我们先来简单回答些如何满足上面的条件。
- 首先明确一定是在Java里面可以供使用者调用的启动线程类是Thread。因此Runnable或者Timer/TimerTask等都是要依赖Thread来启动的,因此在ThreadPool里面同样也是靠Thread来启动多线程的。
- 默认情况下Runnable接口执行完毕后是不能拿到执行结果的,因此在ThreadPool里就定义了一个Callable接口来处理执行结果。
- 为了异步阻塞的获取结果,Future可以帮助调用线程获取执行结果。
- Executor解决了向线程池提交任务的入口问题,同时ScheduledExecutorService解决了如何进行重复调用任务的问题。
- CompletionService解决了如何按照执行完毕的顺序获取结果的问题,这在某些情况下可以提高任务执行的并发,调用线程不必在长时间任务上等待过多时间。
- 显然线程的数量是有限的,而且也不宜过多,因此合适的任务队列是必不可少的,BlockingQueue的容量正好可以解决此问题。
- 固定任务容量就意味着在容量满了以后需要一定的策略来处理过多的任务(新任务),RejectedExecutionHandler正好解决此问题。
- 一定时间内阻塞就意味着有超时,因此TimeoutException就是为了描述这种现象。TimeUnit是为了描述超时时间方便的一个时间单元枚举类。
- 有上述问题就意味了配置一个合适的线程池是很复杂的,因此Executors默认的一些线程池配置可以减少这个操作。
二、线程池Executor的类体系结构与常用线程池
Java里面线程池的顶级接口是Executor,但是严格意义上讲Executor并不是一个线程池,而只是一个执行线程的工具。真正的线程池接口是ExecutorService。
下面这张图完整描述了线程池的类体系结构。
首先Executor的execute方法只是执行一个Runnable的任务,当然了从某种角度上将最后的实现类也是在线程中启动此任务的。根据线程池的执行策略最后这个任务可能在新的线程中执行,或者线程池中的某个线程,甚至是调用者线程中执行(相当于直接运行Runnable的run方法)。这点在后面会详细说明。
ExecutorService在Executor的基础上增加了一些方法,其中有两个核心的方法:
- Future<?> submit(Runnable task)
- <T> Future<T> submit(Callable<T> task)
这两个方法都是向线程池中提交任务,它们的区别在于Runnable在执行完毕后没有结果,Callable执行完毕后有一个结果。这在多个线程中传递状态和结果是非常有用的。另外他们的相同点在于都返回一个Future对象。Future对象可以阻塞线程直到运行完毕(获取结果,如果有的话),也可以取消任务执行,当然也能够检测任务是否被取消或者是否执行完毕。
要配置一个线程池是比较复杂的,尤其是对于线程池的原理不是很清楚的情况下,很有可能配置的线程池不是较优的,因此在
Executors类里面提供了一些静态工厂,生成一些常用的线程池。
- newSingleThreadExecutor:创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
- newFixedThreadPool:创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
- newCachedThreadPool:创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。
- newScheduledThreadPool:创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。
- newSingleThreadScheduledExecutor:创建一个单线程的线程池。此线程池支持定时以及周期性执行任务的需求。
三、线程池Executor的数据结构
由于已经看到了ThreadPoolExecutor的源码,因此很容易就看到了ThreadPoolExecutor线程池的数据结构。下图3描述了这种数据结构。
图3 ThreadPoolExecutor 数据结构
其实,即使没有上述图形描述ThreadPoolExecutor的数据结构,我们根据线程池的要求也很能够猜测出其数据结构出来。
- 线程池需要支持多个线程并发执行,因此有一个线程集合Collection<Thread>来执行线程任务;
- 涉及任务的异步执行,因此需要有一个集合来缓存任务队列Collection<Runnable>;
- 很显然在多个线程之间协调多个任务,那么就需要一个线程安全的任务集合,同时还需要支持阻塞、超时操作,那么BlockingQueue是必不可少的;
- 既然是线程池,出发点就是提高系统性能同时降低资源消耗,那么线程池的大小就有限制,因此需要有一个核心线程池大小(线程个数)和一个最大线程池大小(线程个数),有一个计数用来描述当前线程池大小;
- 如果是有限的线程池大小,那么长时间不使用的线程资源就应该销毁掉,这样就需要一个线程空闲时间的计数来描述线程何时被销毁;
- 前面描述过线程池也是有生命周期的,因此需要有一个状态来描述线程池当前的运行状态;
- 线程池的任务队列如果有边界,那么就需要有一个任务拒绝策略来处理过多的任务,同时在线程池的销毁阶段也需要有一个任务拒绝策略来处理新加入的任务;
- 上面种的线程池大小、线程空闲实际那、线程池运行状态等等状态改变都不是线程安全的,因此需要有一个全局的锁(mainLock)来协调这些竞争资源;
- 除了以上数据结构以外,ThreadPoolExecutor还有一些状态用来描述线程池的运行计数,例如线程池运行的任务数、曾经达到的最大线程数,主要用于调试和性能分析。
四、线程池Executor生命周期
线程池Executor是异步的执行任务,因此任何时刻不能够直接获取提交的任务的状态。这些任务有可能已经完成,也有可能正在执行或者还在排队等待执行。因此关闭线程池可能出现一下几种情况:
- 平缓关闭:已经启动的任务全部执行完毕,同时不再接受新的任务
- 立即关闭:取消所有正在执行和未执行的任务
另外关闭线程池后对于任务的状态应该有相应的反馈信息。
图4 描述了线程池的4种状态。
- 线程池在构造前(new操作)是初始状态,一旦构造完成线程池就进入了执行状态RUNNING。严格意义上讲线程池构造完成后并没有线程被立即启动,只有进行“预启动”或者接收到任务的时候才会启动线程。这个会后面线程池的原理会详细分析。但是线程池是出于运行状态,随时准备接受任务来执行。
- 线程池运行中可以通过shutdown()和shutdownNow()来改变运行状态。shutdown()是一个平缓的关闭过程,线程池停止接受新的任务,同时等待已经提交的任务执行完毕,包括那些进入队列还没有开始的任务,这时候线程池处于SHUTDOWN状态;shutdownNow()是一个立即关闭过程,线程池停止接受新的任务,同时线程池取消所有执行的任务和已经进入队列但是还没有执行的任务,这时候线程池处于STOP状态。
- 一旦shutdown()或者shutdownNow()执行完毕,线程池就进入TERMINATED状态,此时线程池就结束了。
- isTerminating()描述的是SHUTDOWN和STOP两种状态。
- isShutdown()描述的是非RUNNING状态,也就是SHUTDOWN/STOP/TERMINATED三种状态。
图4
线程池的API如下:
图5
其中shutdownNow()会返回那些已经进入了队列但是还没有执行的任务列表。awaitTermination描述的是等待线程池关闭的时间,如果等待时间线程池还没有关闭将会抛出一个超时异常。
对于关闭线程池期间发生的任务提交情况就会触发一个拒绝执行的操作。这是java.util.concurrent.RejectedExecutionHandler描述的任务操作。下一个小结中将描述这些任务被拒绝后的操作。
总结下这个小节:
-
线程池有运行、关闭、停止、结束四种状态,结束后就会释放所有资源
-
平缓关闭线程池使用shutdown()
-
立即关闭线程池使用shutdownNow(),同时得到未执行的任务列表
-
检测线程池是否正处于关闭中,使用isShutdown()
-
检测线程池是否已经关闭使用isTerminated()
-
定时或者永久等待线程池关闭结束使用awaitTermination()操作
五、线程池Executor任务拒绝策略
紧接上面,对于关闭线程池期间发生的任务提交情况就会触发一个拒绝执行的操作。这是java.util.concurrent.RejectedExecutionHandler描述的任务操作。
先来分析下为什么有任务拒绝的情况发生。
这里先假设一个前提:线程池有一个任务队列,用于缓存所有待处理的任务,正在处理的任务将从任务队列中移除。因此在任务队列长度有限的情况下就会出现新任务的拒绝处理问题,需要有一种策略来处理应该加入任务队列却因为队列已满无法加入的情况。另外在线程池关闭的时候也需要对任务加入队列操作进行额外的协调处理。
RejectedExecutionHandler提供了四种方式来处理任务拒绝策略。
这四种策略是独立无关的,是对任务拒绝处理的四种表现形式。
最简单的方式就是直接丢弃任务。但是却有两种方式,到底是该丢弃哪一个任务,比如可以丢弃当前将要加入队列的任务本身(DiscardPolicy)或者丢弃任务队列中最旧任务(DiscardOldestPolicy)。丢弃最旧任务也不是简单的丢弃最旧的任务,而是有一些额外的处理。除了丢弃任务还可以直接抛出一个异常(RejectedExecutionException),这是比较简单的方式。抛出异常的方式(AbortPolicy)尽管实现方式比较简单,但是由于抛出一个RuntimeException,因此会中断调用者的处理过程。除了抛出异常以外还可以不进入线程池执行,在这种方式(CallerRunsPolicy)中任务将有调用者线程去执行。
上面是一些理论知识,下面结合一些例子进行分析讨论。
- package xylz.study.concurrency;
- import java.lang.reflect.Field;
- import java.util.concurrent.ArrayBlockingQueue;
- import java.util.concurrent.ThreadPoolExecutor;
- import java.util.concurrent.TimeUnit;
- import java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy;
- import java.util.concurrent.ThreadPoolExecutor.DiscardPolicy;
- public class ExecutorServiceDemo {
- static void log(String msg) {
- System.out.println(System.currentTimeMillis() + " -> " + msg);
- }
- static int getThreadPoolRunState(ThreadPoolExecutor pool) throws Exception {
- Field f = ThreadPoolExecutor.class.getDeclaredField("runState");
- f.setAccessible(true);
- int v = f.getInt(pool);
- return v;
- }
- public static void main(String[] args) throws Exception {
- ThreadPoolExecutor pool = new ThreadPoolExecutor(1, 1, 0, TimeUnit.SECONDS,
- new ArrayBlockingQueue<Runnable>(1));
- pool.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());
- for (int i = 0; i < 10; i++) {
- final int index = i;
- pool.submit(new Runnable() {
- public void run() {
- log("run task:" + index + " -> " + Thread.currentThread().getName());
- try {
- Thread.sleep(1000L);
- } catch (Exception e) {
- e.printStackTrace();
- }
- log("run over:" + index + " -> " + Thread.currentThread().getName());
- }
- });
- }
- log("before sleep");
- Thread.sleep(4000L);
- log("before shutdown()");
- pool.shutdown();
- log("after shutdown(),pool.isTerminated=" + pool.isTerminated());
- pool.awaitTermination(1000L, TimeUnit.SECONDS);
- log("now,pool.isTerminated=" + pool.isTerminated() + ", state="
- + getThreadPoolRunState(pool));
- }
- }
- package xylz.study.concurrency;
- import java.lang.reflect.Field;
- import java.util.concurrent.ArrayBlockingQueue;
- import java.util.concurrent.ThreadPoolExecutor;
- import java.util.concurrent.TimeUnit;
- import java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy;
- import java.util.concurrent.ThreadPoolExecutor.DiscardPolicy;
- public class ExecutorServiceDemo {
- static void log(String msg) {
- System.out.println(System.currentTimeMillis() + " -> " + msg);
- }
- static int getThreadPoolRunState(ThreadPoolExecutor pool) throws Exception {
- Field f = ThreadPoolExecutor.class.getDeclaredField("runState");
- f.setAccessible(true);
- int v = f.getInt(pool);
- return v;
- }
- public static void main(String[] args) throws Exception {
- ThreadPoolExecutor pool = new ThreadPoolExecutor(1, 1, 0, TimeUnit.SECONDS,
- new ArrayBlockingQueue<Runnable>(1));
- pool.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());
- for (int i = 0; i < 10; i++) {
- final int index = i;
- pool.submit(new Runnable() {
- public void run() {
- log("run task:" + index + " -> " + Thread.currentThread().getName());
- try {
- Thread.sleep(1000L);
- } catch (Exception e) {
- e.printStackTrace();
- }
- log("run over:" + index + " -> " + Thread.currentThread().getName());
- }
- });
- }
- log("before sleep");
- Thread.sleep(4000L);
- log("before shutdown()");
- pool.shutdown();
- log("after shutdown(),pool.isTerminated=" + pool.isTerminated());
- pool.awaitTermination(1000L, TimeUnit.SECONDS);
- log("now,pool.isTerminated=" + pool.isTerminated() + ", state="
- + getThreadPoolRunState(pool));
- }
- }
package xylz.study.concurrency; import java.lang.reflect.Field; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy; import java.util.concurrent.ThreadPoolExecutor.DiscardPolicy; public class ExecutorServiceDemo { static void log(String msg) { System.out.println(System.currentTimeMillis() + " -> " + msg); } static int getThreadPoolRunState(ThreadPoolExecutor pool) throws Exception { Field f = ThreadPoolExecutor.class.getDeclaredField("runState"); f.setAccessible(true); int v = f.getInt(pool); return v; } public static void main(String[] args) throws Exception { ThreadPoolExecutor pool = new ThreadPoolExecutor(1, 1, 0, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(1)); pool.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy()); for (int i = 0; i < 10; i++) { final int index = i; pool.submit(new Runnable() { public void run() { log("run task:" + index + " -> " + Thread.currentThread().getName()); try { Thread.sleep(1000L); } catch (Exception e) { e.printStackTrace(); } log("run over:" + index + " -> " + Thread.currentThread().getName()); } }); } log("before sleep"); Thread.sleep(4000L); log("before shutdown()"); pool.shutdown(); log("after shutdown(),pool.isTerminated=" + pool.isTerminated()); pool.awaitTermination(1000L, TimeUnit.SECONDS); log("now,pool.isTerminated=" + pool.isTerminated() + ", state=" + getThreadPoolRunState(pool)); } }
第一种方式直接丢弃(DiscardPolicy)的输出结果是:
- 1294494050696 -> run task:0
- 1294494050696 -> before sleep
- 1294494051697 -> run over:0 -> pool-1-thread-1
- 1294494051697 -> run task:1
- 1294494052697 -> run over:1 -> pool-1-thread-1
- 1294494054697 -> before shutdown()
- 1294494054697 -> after shutdown(),pool.isTerminated=false
- 1294494054698 -> now,pool.isTerminated=true, state=3
- 1294494050696 -> run task:0
- 1294494050696 -> before sleep
- 1294494051697 -> run over:0 -> pool-1-thread-1
- 1294494051697 -> run task:1
- 1294494052697 -> run over:1 -> pool-1-thread-1
- 1294494054697 -> before shutdown()
- 1294494054697 -> after shutdown(),pool.isTerminated=false
- 1294494054698 -> now,pool.isTerminated=true, state=3
1294494050696 -> run task:0 1294494050696 -> before sleep 1294494051697 -> run over:0 -> pool-1-thread-1 1294494051697 -> run task:1 1294494052697 -> run over:1 -> pool-1-thread-1 1294494054697 -> before shutdown() 1294494054697 -> after shutdown(),pool.isTerminated=false 1294494054698 -> now,pool.isTerminated=true, state=3
对于上面的结果需要补充几点。
- 线程池设定线程大小为1,因此输出的线程就只有一个”pool-1-thread-1”,至于为什么是这个名称,以后会分析。
- 任务队列的大小为1,因此可以输出一个任务执行结果。但是由于线程本身可以带有一个任务,因此实际上一共执行了两个任务(task0和task1)。
- shutdown()一个线程并不能理解是线程运行状态位terminated,可能需要稍微等待一点时间。尽管这里等待时间参数是1000秒,但是实际上从输出时间来看仅仅等了约1ms。
- 直接丢弃任务是丢弃将要进入线程池本身的任务,所以当运行task0是,task1进入任务队列,task2~task9都被直接丢弃了,没有运行。
如果把策略换成丢弃最旧任务(DiscardOldestPolicy),结果会稍有不同。
- 1294494484622 -> run task:0
- 1294494484622 -> before sleep
- 1294494485622 -> run over:0 -> pool-1-thread-1
- 1294494485622 -> run task:9
- 1294494486622 -> run over:9 -> pool-1-thread-1
- 1294494488622 -> before shutdown()
- 1294494488622 -> after shutdown(),pool.isTerminated=false
- 1294494488623 -> now,pool.isTerminated=true, state=3
- 1294494484622 -> run task:0
- 1294494484622 -> before sleep
- 1294494485622 -> run over:0 -> pool-1-thread-1
- 1294494485622 -> run task:9
- 1294494486622 -> run over:9 -> pool-1-thread-1
- 1294494488622 -> before shutdown()
- 1294494488622 -> after shutdown(),pool.isTerminated=false
- 1294494488623 -> now,pool.isTerminated=true, state=3
1294494484622 -> run task:0 1294494484622 -> before sleep 1294494485622 -> run over:0 -> pool-1-thread-1 1294494485622 -> run task:9 1294494486622 -> run over:9 -> pool-1-thread-1 1294494488622 -> before shutdown() 1294494488622 -> after shutdown(),pool.isTerminated=false 1294494488623 -> now,pool.isTerminated=true, state=3
这里依然只是执行两个任务,但是换成了任务task0和task9。实际上task1~task8还是进入了任务队列,只不过被task9挤出去了。
对于异常策略(AbortPolicy)就比较简单,这回调用线程的任务执行。
对于调用线程执行方式(CallerRunsPolicy),输出的结果就有意思了。
- 1294496076266 -> run task:2 -> main
- 1294496076266 -> run task:0 -> pool-1-thread-1
- 1294496077266 -> run over:0 -> pool-1-thread-1
- 1294496077266 -> run task:1 -> pool-1-thread-1
- 1294496077266 -> run over:2 -> main
- 1294496077266 -> run task:4 -> main
- 1294496078267 -> run over:4 -> main
- 1294496078267 -> run task:5 -> main
- 1294496078267 -> run over:1 -> pool-1-thread-1
- 1294496078267 -> run task:3 -> pool-1-thread-1
- 1294496079267 -> run over:3 -> pool-1-thread-1
- 1294496079267 -> run over:5 -> main
- 1294496079267 -> run task:7 -> main
- 1294496079267 -> run task:6 -> pool-1-thread-1
- 1294496080267 -> run over:7 -> main
- 1294496080267 -> run task:9 -> main
- 1294496080267 -> run over:6 -> pool-1-thread-1
- 1294496080267 -> run task:8 -> pool-1-thread-1
- 1294496081268 -> run over:9 -> main
- 1294496081268 -> before sleep
- 1294496081268 -> run over:8 -> pool-1-thread-1
- 1294496085268 -> before shutdown()
- 1294496085268 -> after shutdown(),pool.isTerminated=false
- 1294496085269 -> now,pool.isTerminated=true, state=3
- 1294496076266 -> run task:2 -> main
- 1294496076266 -> run task:0 -> pool-1-thread-1
- 1294496077266 -> run over:0 -> pool-1-thread-1
- 1294496077266 -> run task:1 -> pool-1-thread-1
- 1294496077266 -> run over:2 -> main
- 1294496077266 -> run task:4 -> main
- 1294496078267 -> run over:4 -> main
- 1294496078267 -> run task:5 -> main
- 1294496078267 -> run over:1 -> pool-1-thread-1
- 1294496078267 -> run task:3 -> pool-1-thread-1
- 1294496079267 -> run over:3 -> pool-1-thread-1
- 1294496079267 -> run over:5 -> main
- 1294496079267 -> run task:7 -> main
- 1294496079267 -> run task:6 -> pool-1-thread-1
- 1294496080267 -> run over:7 -> main
- 1294496080267 -> run task:9 -> main
- 1294496080267 -> run over:6 -> pool-1-thread-1
- 1294496080267 -> run task:8 -> pool-1-thread-1
- 1294496081268 -> run over:9 -> main
- 1294496081268 -> before sleep
- 1294496081268 -> run over:8 -> pool-1-thread-1
- 1294496085268 -> before shutdown()
- 1294496085268 -> after shutdown(),pool.isTerminated=false
- 1294496085269 -> now,pool.isTerminated=true, state=3
相关推荐
本书全面解析了Java并发编程的核心概念、原理和实践,帮助读者深入理解多线程并发编程的精髓,提升编程能力和系统性能。 书中首先介绍了并发编程的基础知识,包括线程的基本概念、线程的生命周期、线程安全等问题。...
JAVA多线程之线程间的通信方式.docx Java注解详解.docx Java线程池.docx JDK1.8Stream操作.docx JDK8有新特性.docx JVM堆三代.docx JVM的垃圾回收机制详解和调优.docx Spring源码分析之IoC.docx 关于线程和线程池的...
多线程基础知识 常见关键字 多线程锁机制 线程池知识点 常见的JUC工具类 多线程经典面试题 常用工具集 JVM问题排查工具-JMC IDEA开发神器 线上调试神器-btrace Git原理与工作流程 Linux常用分析工具 数据结构与算法...
│ │ 9.JAVA并发编程之多线程并发同步业务场景与解决方案.wmv │ │ │ ├─10.微服务架构之Spring Cloud Eureka 场景分析与实战 │ │ 10.微服务架构之Spring Cloud Eureka 场景分析与实战.wmv │ │ │ ├─11....
11-线程池 ThreadPoolExecutor 底层原理源码分析(上)-周瑜.pdf 12-线程池 ThreadPoolExecutor底层原理源码分析(下)-周瑜.pdf 13、线程池 ForkJoinPool实战及其工作原理分析 (1).pdf 14、深入理解井发可见性、...
11-线程池 ThreadPoolExecutor 底层原理源码分析(上)-周瑜.pdf 12-线程池 ThreadPoolExecutor底层原理源码分析(下)-周瑜.pdf 13、线程池 ForkJoinPool实战及其工作原理分析 (1).pdf 14、深入理解井发可见性、...
11-线程池 ThreadPoolExecutor 底层原理源码分析(上)-周瑜.pdf 12-线程池 ThreadPoolExecutor底层原理源码分析(下)-周瑜.pdf 13、线程池 ForkJoinPool实战及其工作原理分析 (1).pdf 14、深入理解井发可见性、...
1. 聊聊并发(一)深入分析Volatile的实现原理 2. 聊聊并发(二)Java SE1.6中的Synchronized 3. 聊聊并发(三)Java线程池的分析和使用 4. 聊聊并发(四)深入分析ConcurrentHashMap 5. 聊聊并发(五)原子操作的...
第12章 java多线程技术与应用性能优化 12.1 java多线程技术 12.1.1 进程与线程 12.1.2 线程的生命周期 12.2 并行任务与性能 12.2.1 并行任务与多线程 12.2.2 并行任务与死锁 12.3 线程池技术与应用性能优化 12.3.1 ...
1. 多线程和并发编程:学习使用Java并发包(如java.util.concurrent)处理多线程编程,了解线程池、锁机制和并发集合等。 2. 性能优化:学习分析和调优Java应用程序的性能,了解内存管理、垃圾回收、性能监测和调优...
第1章 Java概述 1 1.1 Java语言的发展简史 2 1.2 Java的竞争对手及各自优势 4 1.2.1 C#简介和优势 4 1.2.2 Ruby简介和优势 4 1.2.3 Python的简介和优势 5 1.3 Java程序运行机制 5 1.3.1 高级语言的运行机制 6...
Java 多线程 多线程中的常见问题 synchronize 关键字原理 多线程的三大核心 对锁的一些认知 ReentrantLock 实现原理 ConcurrentHashMap 的实现原理 线程池原理 深入理解线程通信 交替打印奇偶数 JVM Java 运行时...
Java多线程,线程池,并发库 Java I / O,覆盖绝大部分I / O类 JVM虚拟机技术 深入了解Kotlin技术 Kotlin类型体系 Kotlin面向对象 Lambda表达式 高阶函数 彻底搞懂Kotlin泛型 Kotlin集合 操作符重载 Kotlin协程 ...
│ 高并发编程第一阶段27讲、wait和sleep的本质区别是什么,深入分析(面试常见问题).mp4 │ 高并发编程第一阶段28讲、线程生产者消费者的综合实战结合Java8语法.mp4 │ 高并发编程第一阶段29讲、如何实现一个...
Java 多线程 多线程中的常见问题 synchronize 关键字原理 多线程的三大核心 对锁的一些认知 ReentrantLock 实现原理 ConcurrentHashMap 的实现原理 线程池原理 深入理解线程通信 交替打印奇偶数 JVM Java 运行时...
Java 多线程 多线程中的常见问题 synchronize 关键字原理 多线程的三大核心 对锁的一些认知 ReentrantLock 实现原理 ConcurrentHashMap 的实现原理 线程池原理 深入理解线程通信 交替打印奇偶数 JVM Java 运行时...
│ 高并发编程第一阶段27讲、wait和sleep的本质区别是什么,深入分析(面试常见问题).mp4 │ 高并发编程第一阶段28讲、线程生产者消费者的综合实战结合Java8语法.mp4 │ 高并发编程第一阶段29讲、如何实现一个...
本书深入浅出地介绍了Java线程和并发,是一本完美的Java并发参考手册。书中从并发性和线程安全性的基本概念出发,介绍了如何使用类库提供的基本并发构建块,用于避免并发危险、构造线程安全的类及验证线程安全的规则...
并发与多线程 线程状态转换与通信机制 线程同步与互斥 线程池知识点 常见的JUC工具类 常用工具集 JVM问题排查工具-JMC IDEA开发神器 线上调试神器-btrace Git原理与工作流 Linux常用分析工具 数据结构与算法 从二叉...
11-线程池 ThreadPoolExecutor 底层原理源码分析(上)-周瑜.pdf 12-线程池 ThreadPoolExecutor底层原理源码分析(下)-周瑜.pdf 13、线程池 ForkJoinPool实战及其工作原理分析 (1).pdf 14、深入理解井发可见性、...