池化思想是一种常见软件设计和优化技术。以下是几个常见池化思想应用场景:
线程池:线程池是池化思想一个典型应用。通过预先创建一组线程并将它们置于就绪状态,以复用线程减少线程创建和销毁的开销,并提高系统性能和响应速度。
连接池:连接池是数据库和网络编程中常见应用。通过提前创建一批数据库连接或网络连接并将它们置于可复用状态以避免频繁创建释放连接,从而提高数据库访问和网络通信效率。
对象池:对象池用于管理和重复使用可复用的对象。例如,数据库连接池、线程安全的缓存池等。通过对象池,可以避免频繁创建和销毁对象,提高系统的性能和资源利用率。
内存池:频繁内存分配和释放可能会导致内存碎片,降低内存利用效率。内存池将一块连续内存空间划分为多个固定大小的块并维护一个空闲块列表。程序可以从内存池中申请和释放内存块以减少内存碎片和提高内存的分配性能。
字符串池:字符串池通过将相同的字符串共享在一个池中,减少内存占用和提高字符串比较的速度。Java中字符串常量池就是一种典型字符串池应用。
这些池化思想的应用都旨在提高系统性能、资源利用率和开发效率,避免重复创建和销毁对象,从而降低系统开销,并提供更好的用户体验。
线程池的工作原理可以基本分为以下几个步骤:
初始化线程池:创建线程池管理器,并设置线程池参数,包括线程数量、工作队列大小等。
创建线程:根据线程池初始配置,创建指定数量线程并将它们置于就绪状态等待执行任务。
接收任务:当有任务需要执行时线程池会接收并入队等待处理。任务可以是实现Runnable接口或Callable接口的对象。
任务调度:线程池中的线程从工作队列中获取任务,按照预设的调度策略进行选择,如先进先出(FIFO)、最近最少使用(LRU)等。
执行任务:选中的线程会从工作队列中取出任务,并执行任务的run()或call()方法。任务执行完毕后,线程进入空闲状态,等待下一个任务。
线程回收:当线程池处于空闲状态一段时间后,管理器可能会决定销毁部分线程,以节省系统资源。线程销毁后,线程池的线程数量会相应减少。
错误处理:线程池会捕获任务执行过程中产生的异常,防止线程因未捕获的异常而崩溃。异常处理策略可以根据具体需求进行自定义,如重新执行、记录日志、忽略等。
通过上述工作原理,线程池实现了线程的复用和资源的合理利用,使得系统能够更好地处理并发任务,提高性能和响应速度。同时,线程池还能控制线程数量,避免过多的线程造成资源消耗和性能下降,提供了对线程的管理机制。
线程池是一种用于管理和复用线程的机制。它由一个线程池管理器、工作队列和一组线程组成。线程池管理器负责创建、销毁和监控线程池中的线程。它根据系统负载和预设条件来动态地调整线程数量,保证线程池的高效利用和合理分配。工作队列用于存储待处理的任务。线程池中的线程从工作队列中取出任务并执行。当任务数量超过线程池的处理能力时,任务会被暂时放入队列中,待有空闲线程时再进行处理。
线程池的推出,就是为了方便边的控制线程数量。
使用线程池时,需要考虑线程数量、工作队列大小以及任务调度策略等因素,以确保线程池能够按预期工作并提供最佳的性能。同时,我们还要注意处理异常情况,避免线程由于未捕获的异常而导致整个线程池崩溃。
阿里巴巴Java开发手册,明确指出不允许使用Executors静态工厂构建线程池,原因如下:
线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险,说明:Executors返回的线程池对象的弊端如下:
1:FixedThreadPool 和 SingleThreadPool:
允许的请求队列(底层实现是LinkedBlockingQueue)长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM
2:CachedThreadPool 和 ScheduledThreadPool
允许的创建线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM。
创建线程池的正确姿势:避免使用Executors创建线程池,主要是避免使用其中的默认实现,那么我们可以自己直接调用ThreadPoolExecutor的构造函数来自己创建线程池。在创建的同时,给BlockQueue指定容量就可以了。
private static ExecutorService executor=newThreadPoolExecutor(10,10,60L,TimeUnit.SECONDS,new ArrayBlockingQueue(10));
或者是使用开源类库:开源类库,如apache和guava等。
线程池通常使用一种称为"Future"的机制来跟踪线程任务的执行状况。
在Java中,可以通过java.util.concurrent.Future
接口来表示异步计算的结果。线程池提交的任务可以返回一个Future对象,在任务执行完成后,可以通过该对象获取任务的执行结果或判断任务是否完成。
具体而言,可以按照以下步骤使用Future来知道线程任务是否已经执行完成:
提交任务:将要执行的任务提交给线程池,并获得返回的Future对象。
判断任务状态:通过调用Future的isDone()
方法判断任务是否已经完成。如果任务完成,返回true;否则返回false。
获取任务结果:如果任务已经完成,可以通过调用Future的get()
方法获取任务的执行结果。该方法会阻塞当前线程,直到任务完成并返回结果。
需要注意的是,如果任务还未完成,调用get()
方法会使当前线程阻塞,直到任务执行完成。如果不希望阻塞当前线程,可以使用isDone()
方法轮询判断任务状态,或者使用带有超时参数的get()
方法来等待一段时间后获取结果。
总之,通过使用Future对象,线程池可以方便地跟踪和获取线程任务的执行状态和结果,从而实现对任务的管理和控制。
Java 中线程池的 7 种创建方式! - 磊哥|www.javacn.site - 博客园 (cnblogs.com)
Java线程池实现原理及其在美团业务中的实践 - 美团技术团队 (meituan.com)
JUC线程池: ThreadPoolExecutor详解 | Java 全栈知识体系 (pdai.tech)
线程池面试题 - 小魚人 - 博客园 (cnblogs.com)