线程池(ThreadPool)
线程池详解
- 线程池的作用
线程池是一种线程使用模式,它主要解决两个问题:
重复利用线程:避免频繁创建和销毁线程,减少系统开销。
控制线程数量:限制线程的最大并发数,避免系统资源过度消耗。
其他优势包括:
提高响应速度:重用线程避免了线程创建的延迟。
提高线程的可管理性:统一管理,方便监控。
2. 线程池的创建
2.1 使用Executors工厂方法
Java提供了Executors类,其中包含多个静态工厂方法来创建不同类型的线程池:
ExecutorService executorService = Executors.newFixedThreadPool(2); // 这里的线程池里面的线程数量为2
1
常用的工厂方法包括:
newFixedThreadPool(int nThreads): 创建固定大小的线程池
newCachedThreadPool(): 创建可缓存的线程池
newSingleThreadExecutor(): 创建单线程的线程池
newScheduledThreadPool(int corePoolSize): 创建可以执行定时任务的线程池
2.2 手动创建ThreadPoolExecutor(拓展)
对于更精细的控制,可以直接使用ThreadPoolExecutor类:
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.SECONDS,
new LinkedBlockingQueue()
);
1
2
3
4
3. 线程池的使用
3.1 提交任务
可以通过execute()方法提交Runnable任务:
executorService.execute(new Runnable() {
@Override
public void run() {
// 任务代码
}
});
1
2
3
4
5
6
或者使用submit()方法提交Callable任务(可以有返回值)。
3.2 关闭线程池
shutdown(): 温和地关闭线程池,等待所有任务完成。
shutdownNow(): 立即关闭线程池,尝试中断正在执行的任务。
executorService.shutdown();
// 或
// executorService.shutdownNow();
1
2
3
4. 依赖
线程池相关的类都在java.util.concurrent包中,是Java标准库的一部分,不需要额外的依赖。
主要用到的类:
java.util.concurrent.ExecutorService
java.util.concurrent.Executors
java.util.concurrent.ThreadPoolExecutor
5. 线程池的参数
使用ThreadPoolExecutor时,可以设置以下参数:
corePoolSize: 核心线程数
maximumPoolSize: 最大线程数
keepAliveTime: 线程空闲时间
workQueue: 工作队列,用于存放任务
threadFactory: 线程工厂,用于创建线程
handler: 拒绝策略,当任务太多时如何处理
6. 注意事项
避免使用Executors创建线程池,因为它可能导致OOM(内存溢出)。推荐直接使用ThreadPoolExecutor。
合理设置线程池大小,考虑CPU核心数、内存大小等因素。
注意监控线程池的状态,及时调整参数。
记得在应用程序结束前关闭线程池。
7. 示例代码解析
ExecutorService executorService = Executors.newFixedThreadPool(2);
1
创建了一个固定大小为2的线程池。
for (int i = 0; i < 5; i++){
// 创建并提交任务
}
1
2
3
循环创建5个任务并提交给线程池。
executorService.shutdown();
1
关闭线程池,等待所有任务完成。
这个示例展示了如何创建线程池、提交任务和关闭线程池的基本操作。
- 具体实例
连接池
数据库连接池详解
- 连接池的作用
数据库连接池主要解决以下问题:
提高性能:避免频繁地创建和关闭数据库连接,减少系统开销。
资源管理:限制数据库连接的数量,防止数据库服务器过载。
提高可用性:预先创建连接,减少获取连接的等待时间。
连接重用:允许多个请求共享同一个连接,提高资源利用率。
2. 连接池的实现(基于提供的代码)
2.1 使用 Druid 连接池
private static DruidDataSource ds;
static {
ds = new DruidDataSource();
ds.setDriverClassName(“com.mysql.cj.jdbc.Driver”);
ds.setUrl(“jdbc:mysql://localhost:3306/jdbc_study?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true”);
ds.setUsername(“root”);
ds.setPassword(“root”);
ds.setMaxActive(20); // 最大连接数
ds.setInitialSize(5); // 初始化连接数
}
1
2
3
4
5
6
7
8
9
10
11
这段代码初始化了 Druid 连接池,设置了数据库驱动、URL、用户名、密码等基本信息,以及最大连接数和初始连接数。
2.2 获取连接
public static Connection getConnection() throws Exception {
return ds.getConnection();
}
1
2
3
这个方法从连接池中获取一个连接。当调用此方法时,如果池中有可用连接,就直接返回;如果没有,可能会创建新的连接(取决于池的配置)。
-
连接池的优势
性能提升:重用连接减少了创建和销毁连接的开销。
资源控制:可以限制最大连接数,防止数据库过载。
响应速度:预创建连接减少了等待时间。
统一管理:集中管理数据库连接,便于监控和调优。 -
应用场景
高并发web应用:大量用户同时访问数据库时。
数据密集型应用:需要频繁数据库操作的场景。
分布式系统:多个服务需要共享数据库资源时。
微服务架构:每个微服务可能需要独立的数据库连接池。 -
连接池的关键参数
MaxActive:最大活跃连接数
InitialSize:初始连接数
MinIdle:最小空闲连接数
MaxWait:获取连接最大等待时间 -
最佳实践
合理设置池大小:根据系统并发量和数据库性能调整。
监控连接池状态:关注连接使用情况,及时调整参数。
使用预编译语句:可以配合连接池使用,进一步提高性能。
及时释放连接:使用完毕后及时将连接返回池中。 -
注意事项
避免连接泄露:确保在 finally 块中关闭连接。
定期检查空闲连接:移除长时间不用的连接。
考虑使用连接池框架:如 Druid、HikariCP 等,它们提供了更多高级特性。 -
代码中的改进点
可以考虑将数据库配置信息外部化,例如放在配置文件中。
添加更多的连接池配置,如最小空闲连接数、连接最大存活时间等。
实现一个关闭连接池的方法,在应用程序结束时调用。
通过使用连接池,您的应用程序可以更高效地管理数据库连接,提高性能和可扩展性。在实际应用中,需要根据具体场景调整连接池的参数,以达到最佳效果。 -
代码参考示例
package socket;
import com.alibaba.druid.pool.DruidDataSource;
import java.sql.Connection;
import java.sql.DriverManager;
public class DBUtil {
// 静态连接池
private static DruidDataSource ds;
//数据库工具类
static{
/*
try{
// 反射机制获取类对象
Class.forName("com.mysql.cj.jdbc.Driver");
//
}catch (Exception e){
e.printStackTrace();
}*/
// 实例化连接池
ds = new DruidDataSource();
ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
String databaseName = "jdbc_study";
ds.setUrl("jdbc:mysql://localhost:3306/"+databaseName+"?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true");
ds.setUsername("root");
ds.setPassword("root");
ds.setMaxActive(20); // 最大连接数
ds.setInitialSize(5); // 初始化连接数
}
// 静态方法获取连接
// 之所以使用throw Exception,是为了让调用者处理异常,而不是创建者处理异常
public static Connection getConnection() throws Exception{
// 在静态块中加载驱动
/*// 返回获取的连接
String databaseName = "jdbc_study";
return DriverManager.getConnection(
"jdbc:mysql://localhost:3306/"+databaseName+"?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true",
"root",
"root"
);*/
// 返回连接池中的连接
return ds.getConnection();
}
}