Java面试题~Java线程池实战总结一之百万数据的批量插入

摘要

对于从事Java开发的小伙伴而言,“线程池”一词应当不陌生,虽然在实际工作、项目实战中可能很少用过,但是在工作闲暇或吹水之余还是会听到他人在讨论,更有甚者,在跳槽面试等场合更是屡见不鲜,已然成为一道“必面题”。从本文开始我们将开启“Java线程池实战总结”系列文章的分享,帮助各位小伙伴认识、巩固并实战线程池的相关技术要点。

内容

  • 吹一波“线程池”

“线程池”,字如其名,是“线程”+“池”合并得来的,“线程”的含义自然不用多说,而“池”其实是一种“设计思想”,即池化设计技术,这种思想简而言之是为了能更好地重复利用资源,减少因资源重复创建而带来的损耗,提高资源重复利用率。

“池化设计”带来的产物有很多,目前在市面上我们经常可以见到的有“数据库连接池”、“Http连接池”、“Redis连接池”以及我们本文要介绍的“线程池”等等,都是对这种设计思想的应用。

线程池提供了一种限制和管理“线程”资源的机制,池里面的每个线程所承担的职责无非就是执行项目、程序中待执行的任务,当待执行的任务数量大于1时,线程池中预先分配好的多个线程便起到了作用,当执行完任务后,线程并不会立即被Kill掉,而是会保留在线程池一段时间,等待下次被重复利用。   

  • 基于ThreadPoolExecutor实现百万数据的批量插入

接下来,我们以实际项目中典型的业务场景“多线程批量插入随机生成的200w条数据”为案例,初步认识并了解Java中如何使用线程池 ThreadPoolExecutor 做一些事情。

(1)首先,我们得先定义这些事情,也就是“任务” 是什么,特别是要让我们的系统、线程知道待执行的每个“任务”是什么,很显然,我们的任务是“插入数据”,而这些数据将插入数据库表 book中,该数据库表的DDL定义如下所示:

CREATE TABLE `book` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '书名',
  `author` varchar(255) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '作者',
  `release_date` date DEFAULT NULL COMMENT '发布日期',
  `is_active` tinyint(255) DEFAULT '1' COMMENT '是否有效',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='书籍信息表';

而这个任务其实可以通过 实现 Runnable 接口或者 Callable 接口来定义,只有通过这种方式定义,我们的系统才认识,如下代码所示:

class insertTask implements Callable<Boolean>{
    @Override
    public Boolean call() throws Exception {
        Book b;
        String msg;
        for (int i=0;i<50000;i++){
            b=new Book();
            msg=snowflake.nextIdStr();
            b.setName(msg);
            b.setAuthor(msg+"-A");
            b.setReleaseDate(new Date());
            bookMapper.insertSelective(b);
        }
        return true;
    }
}

在上述代码中,我们定义了每个线程待执行的“任务”:循环插入 5w 条数据,其中,为了避免多线程并发生成相同的数据,我们采用了雪花算法工具生成了“全局唯一ID”;而200w的数据,需要在外部循环遍历 40次,如果用传统的“单一线程”进行实现,很显然是行不通的(那得插到天昏地暗才能完成….)

(2)有了任务,那么接下来就需要有“线程”去执行,在这里我们需要定义线程执行器 ThreadPoolExecutor 实例,讲给这位大佬安排处理,即用于在线程池创建 N 个线程,并用于执行执行这 40个 任务,其源码如下所示:

@Autowired
private BookMapper bookMapper;

private ArrayBlockingQueue queue=new ArrayBlockingQueue(8,true);

private ThreadPoolExecutor.CallerRunsPolicy policy=new ThreadPoolExecutor.CallerRunsPolicy();

private ThreadPoolExecutor executor=new ThreadPoolExecutor(4,8,10, TimeUnit.SECONDS
        ,queue,policy);

private static final Snowflake snowflake=new Snowflake(3,2);

@Test
public void testBatchInsert() throws Exception{
    List<insertTask> tasks= Lists.newLinkedList();
    for (int j=0;j<40;j++){
        tasks.add(new insertTask());
    }
    executor.invokeAll(tasks);
}

点击运行该单元测试方法,之后需要等待一定的时间,最终你会在IDEA控制台看到同时会有好几个线程并行插入数据,在数据库表中也最终可以看到其成功插入了 200w 条数据,如下图所示:   

更多请见:http://www.mark-to-win.com/tutorial/51082.html 

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在 Java 中,有多种方法实现数据批量插入,其中常用的方法如下: 1. JDBC Statement: 使用 JDBC Statement 可以通过设置 addBatch() 和 executeBatch() 方法实现批量插入。 2. JDBC PreparedStatement: 使用 JDBC PreparedStatement 可以通过设置 addBatch() 和 executeBatch() 方法实现批量插入,比 Statement 效率更高。 3. Hibernate: Hibernate 是一个开源的 Java 持久层框架,支持批量插入。 4. JPA: JPA 是 Java Persistence API 的缩写,是 Java 持久层的标准规范,支持批量插入。 以上四种方法都可以帮助你实现数据批量插入,你可以根据自己的需求选择合适的方法进行实现。 ### 回答2: 在Java中,要实现数据批量插入,可以使用JDBC(Java Database Connectivity)来操作数据库。 首先,需要建立数据库连接。可以使用JDBC提供的DriverManager类的getConnection方法来创建数据库连接。示例代码如下: ```java String url = "jdbc:mysql://localhost:3306/mydatabase"; String username = "root"; String password = "password"; Connection conn = DriverManager.getConnection(url, username, password); ``` 接下来,可以使用PreparedStatement类来创建可执行SQL语句的实例,并设置参数。在执行批量插入时,可以使用addBatch方法来向批处理中添加多个SQL语句。示例代码如下: ```java String insertQuery = "INSERT INTO mytable (column1, column2) VALUES (?, ?)"; PreparedStatement pstmt = conn.prepareStatement(insertQuery); pstmt.setString(1, "value1"); pstmt.setString(2, "value2"); pstmt.addBatch(); // 添加第一条记录 pstmt.setString(1, "value3"); pstmt.setString(2, "value4"); pstmt.addBatch(); // 添加第二条记录 // 添加其他记录... pstmt.executeBatch(); // 执行批量插入 ``` 最后,记得在操作完成后关闭数据库连接。 ```java pstmt.close(); conn.close(); ``` 通过以上步骤,可以使用Java实现数据批量插入。此方法可以提高数据插入效率,减少与数据库的通信次数,从而极大地提升程序的性能。 ### 回答3: 在Java中实现数据批量插入可以使用数据库的批处理操作来提高插入效率。以下是一种常见的实现方式: 1. 使用JDBC连接到数据库:首先需要使用Java的JDBC API连接到数据库。这可以通过配置数据库连接信息和加载合适的驱动程序来完成。 2. 创建插入语句:根据需要插入数据,创建合适的插入语句。可以使用预编译的SQL语句,以便在插入多个数据时只需编译一次,并且可以重复使用。 3. 设定批处理大小:根据需要插入数据数量,设定每次插入的批处理大小。较大的批处理大小可以提高插入效率,但同时也会消耗更多的内存。 4. 创建批处理操作:使用Java的JDBC API,创建批处理操作对象,关联到连接的数据库和插入语句。 5. 设置参数并添加到批处理操作:为每个需要插入数据,设置合适的参数,并将操作添加到批处理中。 6. 执行批处理操作:在准备好所有需要插入数据后,使用批处理操作的executeBatch()方法来执行批处理插入操作。 7. 处理插入结果:根据需要处理插入结果,例如检查插入是否成功等。 8. 关闭资源:在插入完成后,关闭所有的数据库资源,包括连接和批处理操作等。 使用批处理插入可以减少与数据库的通信次数,从而提高插入效率。但需要注意的是,批处理插入数据量过大可能会导致内存溢出,因此需要根据实际情况设置合适的批处理大小。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值