数据库连接池的概念
我们之前学习过的数据库连接池,在某种程度上和Java线程池是思路一致的。
因为频繁的创建数据库的连接对象,又销毁,在时间上开销较大。所以使用集合存储一些 事先创建出的连接对象,每次使用时,从集合中获取,用完不销毁,减少频繁创建,又销毁;
数据库连接池、线程池 意思都是类似的;
线程池是什么?
它是一种并发编程的技术,用于管理并发执行的线程。他预先创建一组线程,并维护一个任务队列,用于存储需要执行的任务。当有任务到达时,线程池从队列中获取一个空闲的线程来执行任务,当任务执行完毕后,线程将返回线程池中,等待下一个任务。
线程池的主要优点是提高了线程的复用性和性能。通过预先创建一组线程,并且避免了线程的创建和销毁开销,可以减少系统的开销,提高任务的执行效率。线程池可以控制同时执行的线程数量,避免系统资源被过度消耗,可以有效地管理系统的并发度。
线程池通常用于多线程的场景,例如服务器端的请求处理、并发任务处理等。它可以使程序更稳定、可伸缩,并且能够提供更好的性能和资源管理。
为什么会有线程池?
-
减少线程创建和销毁的开销:创建和销毁线程是一个比较消耗资源的操作,频繁地创建和销毁线程会增加系统开销。而线程池通过事先创建一组线程,并将其保存在池中,可以避免频繁地创建和销毁线程,提高系统性能和资源利用率。
-
控制并发度:线程池可以限制线程的数量,通过设置线程池的大小,可以控制同时执行的线程数。这样可以避免线程数量过多导致系统资源耗尽,避免过度竞争和阻塞,提高系统的稳定性和吞吐量。
-
提高任务处理效率:线程池能够将任务进行排队,当有任务到达时,线程池会从队列中获取一个可用的线程来执行任务。通过任务队列,线程池可以有效地管理任务的执行顺序和调度,避免任务之间的冲突和竞争,并且可以提高任务的执行效率。
-
提供线程管理和监控机制:线程池可以提供对线程的管理和监控机制,例如可以统计线程的执行情况、线程的状态和健康状况等信息。这些信息可以用于系统性能优化、故障排查和监控报警等目的。
综上所述,引入线程池可以提高系统的性能和稳定性,优化资源利用,提高任务处理效率,同时提供线程管理和监控机制,更方便地管理和调优系统。因此,线程池在多线程编程中得到了广泛的应用。
线程池是什么结构?
实质是集合,存储的线程
首先,线程池并不是像堆、栈那样的存储空间。线程池是一种并发编程的技术,用于管理并发执行的线程。它实际上是一个线程的集合,其中包含了多个预先创建的线程。
线程池内部通常维护一个任务队列,用于存储需要执行的任务。当有任务到达时,线程池会从队列中获取一个空闲的线程来执行该任务。任务执行完毕后,线程将返回线程池中等待下一个任务。
线程池的主要目的是提高线程的复用性和性能,减少线程的创建和销毁开销,并且控制并发度,以提高系统的稳定性和资源利用率。虽然线程池可能需要一定的存储空间来存储线程池对象、任务队列等数据结构,但线程池本身并不是用来存储数据的存储空间。
线程池主要用于什么地方?
线程池主要用于在多线程编程中管理和调度线程,提高并发任务的执行效率和系统的性能。以下是一些常见的应用场景:
-
服务器端编程:在服务器端应用程序中,通常需要同时处理多个客户端请求。使用线程池可以控制并发度,避免过多的线程同时处理请求,从而提高服务器的吞吐量和响应速度。
-
Web应用程序中的请求处理:在Web开发中,服务器需要同时处理多个客户端请求。使用线程池可以解决并发请求的处理问题,并提供更好的性能和资源管理。
-
计算密集型任务:在需要进行大量计算的任务中,可以将任务分解为多个子任务,然后使用线程池并行地执行这些子任务,以提高计算效率。
-
I/O密集型任务:在进行大量I/O操作,例如读写文件、网络通信等任务时,使用线程池可以实现异步操作,提高系统的吞吐量和响应性能。
-
并发数据处理:在并发数据处理场景中,例如数据抓取、数据清洗、数据分析等,可以使用线程池处理并行的数据处理任务,提高处理效率和吞吐量。
总之,任何需要并发执行任务的场景都有可能使用到线程池。线程池能够控制线程的数量、调度任务的执行,并提供线程管理和监控机制,以满足不同应用场景的需求。
具体例子:
-
Web服务器:Web服务器需要同时处理多个客户端的请求。使用线程池可以管理并发请求的处理,避免创建过多的线程导致资源耗尽,提高服务器的性能和稳定性。
-
数据库连接池:在数据库访问场景中,连接数据库是一个开销较大的操作。使用线程池可以预先创建一组数据库连接,并将其保存在连接池中。当有请求到达时,线程池提供可复用的数据库连接,避免了频繁的数据库连接和断开操作,提高数据库访问的效率。
线程池
jdk5之后java增加了线程池的实现,使用ThreadPoolExecutor类实现线程池创建和管理.
池的好处
减少频繁创建销毁时间,统一管理线程,提高速度。
ThreadPoolExecutor类图
通常使用ThreadPoolExecutor
1.线程池状态
使用ctl变量存储线程池的状态
ThreadPoolExecutor使用int的高3位来表示线程池状态,低29位表示线程数量
为什么要将线程池状态和线程数量存在一个变量中?
这些信息存储在一个原子变量ctl中,目的是将线程池状态与线程个数合二为一,这样就可以用一次cas原子操作进行赋值。
2.ThreadPoolExcutor构造方法
-
corePoolSize核心线程数目(最多保留的线程数)
-
maximumPoolSize最大线程数目
-
keepAliveTime生存时间-针对救急线程
-
unit时间单位-针对救急线程
-
workQueue阻塞队列;他也有大小
-
threadFactory线程工厂-可以为线程创建时起个好名字;可以把线程池中的线程和其他线程区分开来
-
handler拒绝策略
工作方式
c为核心线程数,m为最大线程数(核心+救急)
核心线程数就是线程池一次可以执行的线程数目,而如果线程池已经达到核心线程数了,还有任务过来,那么这线程就会进入阻塞队列中,如果阻塞队列也满了,还有任务过来,那么就需要救急线程出动了,救急线程执行完任务后,就会销毁,结束,下次高峰期来了,再重新创建(核心线程运行完任务了,还会保留在线程池);在此期间,线程池中所有线程都已经有任务,救急线程都已经满员时,此时若再来任务,才进行拒绝策略。
具体一点:
3.newFixedThreadPool
fixed固定的意思,即固定线程数的线程池,指的是核心线程数固定,而阻塞队列的数量是无限的。
特点:
-
核心线程数==最大线程数(没有救急线程被创建)因此也无需超时时间
-
阻塞队列是无界的,可以放任意数量的任务
评价:适用于任务量已知,相对耗时的任务。
4.newCachedThreadPool
带缓冲功能的线程池
特点
-
核心线程数为0,最大线程数是Integer.MAX_VALUE,救急线程的空闲生存时间是60s,意味着
-
全部都是救急线程(60s后可以回收)
-
救急线程可以无限创建
-
-
队列采用了SynchronizedQueue实现,特点是,它没有容量,没有线程来取是放不进去的(一手交钱,一手交货)
评价:整个线程池表现为线程数会根据任务量不断增长,没有上限,当任务执行完毕,空闲一分钟后释放线程。
适合任务数比较密集,但每个任务执行时间较短的情况
5.newSingleThreadExcutor
单线程执行器,线程池中只有一个使用场景核心线程,没有救急线程,可以从下面的参数看出
使用场景:
希望多个任务排队执行。线程数固定为1,任务数多于1时,会放入无界队列排队。任务执行完毕,这唯一的线程也不会被释放。
自己创建一个线程和用单线程池的区别?
-
自己创建一个单线程串行执行任务,如果任务执行失败而终止那么没有任何补救措施,而线程池还会新建一个线程,保证池的正常工作
-
Executors.newSingleThreadExecutor()线程个数始终为1.不能改变
-
FinalizableDelegatedExecutorService应用的是装饰器模式,只对外暴露了ExecutorService接口,因此不能调用ThreadPoolExecutor中特有的方法
-
Executors.newFixedThreadPool(1)初始时为1,以后还可以修改
-
对外暴露的是ThreadPoolExecutor对象,可以强转后调用setCorePoolSize等方法进行修改
-
6.线程池提交任务
参数是runnable接口,相当于传进来一个线程要执行的内容
7.关闭线程池
shutdown()方法
线程池状态变为SHUTDOWN
-
不会接收新任务
-
但已提交任务会执行完
-
此方法不会阻塞调用线程的执行
shutdownNow()方法
线程池状态变为STOP
List<Runnable> shutdownNow();
-
不会接收新任务
-
会将队列中的任务返回
-
并用interrupt的方式中断正在执行的任务
其他方法