问题1:
ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler)
其中: RejectedExecutionHandler handler 是什么意思?
在 ThreadPoolExecutor 的构造函数中,RejectedExecutionHandler 参数用于处理当工作队列已满并且线程池中的线程数已经达到最大值时,新任务无法继续提交到线程池中时的情况。这种情况也被称为线程池的饱和状态。
RejectedExecutionHandler 是一个接口,用于定义当任务无法提交到线程池时的处理策略。当工作队列已满并且线程池中的线程数已经达到最大值时,线程池会使用 RejectedExecutionHandler 所定义的策略来处理新任务。例如,ThreadPoolExecutor 类提供了以下四种预定义的 RejectedExecutionHandler 实现:
- AbortPolicy: 默认的处理策略,当工作队列已满并且线程池中的线程数已经达到最大值时,会直接抛出一个 RejectedExecutionException 异常,表示新任务无法继续提交到线程池中;
- CallerRunsPolicy: 当工作队列已满并且线程池中的线程数已经达到最大值时,会将新任务提交到当前线程中执行,也就是将任务执行权交给提交任务的线程;
- DiscardPolicy: 当工作队列已满并且线程池中的线程数已经达到最大值时,会直接将新任务丢弃,不做任何处理;
- DiscardOldestPolicy: 当工作队列已满并且线程池中的线程数已经达到最大值时,会先丢弃工作队列中最早被提交的任务,然后再将新任务提交到工作队列中。
通过提供自定义的 RejectedExecutionHandler 实现,我们可以在线程池的饱和状态下实现自己的处理策略,以更好地适应特定的业务需求。
为什么这个方法没有 ThreadFactory参数?
ThreadPoolExecutor 类的另一个构造函数没有提供 ThreadFactory 参数,是因为该构造函数会使用默认的 ThreadFactory 实现来创建线程池中的线程。默认情况下,ThreadPoolExecutor 使用的是 Executors.defaultThreadFactory() 方法返回的默认线程工厂。这个默认线程工厂创建的线程名称格式为 "pool-" + poolNumber + "-thread-" + threadNumber,其中 poolNumber 是线程池的编号,threadNumber 是线程的编号。
当然,如果需要自定义线程工厂,可以使用另一个构造函数,并传入自定义的 ThreadFactory 对象。这个自定义的 ThreadFactory 对象可以根据自己的需要来创建线程。
问题2:
ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory)
其中 ThreadFactory threadFactory是什么意思?
在 ThreadPoolExecutor 的构造函数中,ThreadFactory 参数用于创建新线程时的工厂对象。当线程池中的线程数不足以处理新任务时,线程池会使用 ThreadFactory 所创建的新线程来处理任务。
ThreadFactory 是一个接口,用于定义如何创建新的线程对象。具体来说,它只有一个方法 newThread,用于创建新线程。当线程池需要创建新的线程时,会调用 ThreadFactory 对象的 newThread 方法来创建新线程对象,并将新线程添加到线程池中。
通过自定义 ThreadFactory 实现,我们可以控制线程的一些属性,例如线程名字、是否为守护线程等。通常情况下,我们可以使用 ThreadFactory 的默认实现类 DefaultThreadFactory,它会创建一些符合标准的、没有特殊需求的线程对象。但是在一些特殊场景下,我们可能需要自定义线程对象的属性,此时可以通过实现自己的 ThreadFactory 来满足需求。例如,我们可以自定义线程名字,让日志打印更加方便。
new ThreadPoolExecutor(
5,
10,
// 如果60秒接收不到任务则杀死线程
60L,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(10000),
r -> new Thread(r, "kjds-measure-thread-pool-" + new Thread(r).getId())) 请普通的语法看看
ThreadFactory threadFactory = new ThreadFactory() { @Override public Thread newThread(Runnable r) { Thread thread = new Thread(r); thread.setName("kjds-measure-thread-pool-" + thread.getId()); return thread; }};Executor executor = new ThreadPoolExecutor( 5, // corePoolSize 10, // maximumPoolSize 60L, // keepAliveTime TimeUnit.SECONDS, // unit new LinkedBlockingQueue<>(10000), // workQueue threadFactory // threadFactory);这段代码与使用 lambda 表达式的代码功能完全一致,只是将 lambda 表达式转换成了等价的匿名内部类语法。在这个等价的代码中,创建了一个实现了 ThreadFactory 接口的匿名内部类,并实现了其中的 newThread 方法。在 ThreadPoolExecutor 的构造函数中,将创建的 ThreadFactory 对象作为 threadFactory 参数传入。这样就可以使用该 ThreadFactory 对象创建线程池中的线程了。
如果有 ThreadFactory 并创建了线程池,请问该如何去 观察多线程呢?
在 Java 中,有多种方法可以观察多线程的状态。以下是几种常用的方法:
jstack 命令可以打印出指定进程的线程堆栈信息。在命令行中输入 jstack <pid>,其中 <pid> 是进程 ID,即可打印出该进程中所有线程的堆栈信息。可以通过分析线程堆栈信息来了解每个线程的状态,比如是否在等待某个资源、是否在运行某个方法等等。
VisualVM 是一款免费的 Java 监控和调优工具,可以用来观察多线程的状态。在 VisualVM 中,可以通过 Threads 标签页来查看进程中所有线程的状态,包括线程的状态、线程 ID、线程名、线程所属的类、线程堆栈信息等等。
Java 提供了一些用于观察多线程的工具类,比如 ThreadMXBean、LockInfo、MonitorInfo 等等。可以使用这些工具类来获取线程的状态、堆栈信息、锁信息等等。
无论使用哪种方法,都需要注意多线程状态的变化是动态的,需要多次观察才能准确地了解多线程的状态。同时,也需要注意观察多线程的成本比较高,可能会对系统性能产生影响,因此应该在必要的时候才进行观察。