java 线程池、多线程并发实战(生产者消费者模型 1 vs 10) 附案例源码
线程池
为什么要使用线程池
例如web服务器、数据库服务器、文件服务器或邮件服务器之类的。请求的时候,单个任务时间很短,但是请求数量巨大。每一次请求,就会创建一个新线程,然后在新线程中请求服务,频繁的创建线程,销毁线程造成系统很大的开销,资源的浪费。
线程池为线程生命周期开销问题和资源不足问题提供了解决方案。通过对多个任务重用线程,线程车创建的开销分摊到多个任务上。
创建与使用
Future
对具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果、设置结果。get方法会阻塞,直到任务返回结果。
Callable&FutureTask
Callable与Runnable功能相似,Callable有返回值;Runnable没有返回值;一般情况下,Callable与FutureTask一起使用,或者与线程池一起使用
线程池核心组成部分
- corePoolSize:核心线程池大小
- maximumPoolSize:线程池最大容量
- KeepAliveTime:当线程数量大于核心时,多余的空闲线程在终止之前等待新任务的最大时间
- unit:时间单位
- workQueue:工作队列
- ThreadFactory:线程工厂
- handler:拒绝策略
Executor框架
实战
需求分析
业务场景
一般系统,多数会与第三方系统的数据进行打交道,而第三方的生产库,并不允许我们直接操作。在企业里面,一般都是通过中间表进行同步,即第三方系统将生产数据放入一张与其生产环境隔离的另一个独立数据库中的独立表,在根据接口协议,增加相应的字段。而我方需要读取该中间表中的数据,并对数据进行同步操作。此时就需要编写相应的程序进行数据同步。
同步方式
- 全量同步:每天定时将当天的生产数据全部同步过来(优点:实现检点;缺点:数据同步不及时)
- 增量同步:每新增一条,便将该数据同步过来(优点:数据接近实时同步;缺点:实现相对困难)
我方需要做的事情
读取中间表的数据,并同步到业务系统中
模型抽离(生产者消费者模型)
- 生产者:读取中间表的数据
- 消费者:消费生产者生产的数据
接口协议的制定
- 取我方业务上需要用到的字段
- 需要有字段记录数据什么时候进入中间表
- 增加相应的数据标志位,用于标志数据的同步状态
- 记录数据的同步时间
技术选型
- mybatis
- 单一生产者多消费者
- 多线程并发操作
中间表设计
项目搭建
项目结构
pom.xml
pom.xml
log4j.properties
log4j.properties
middle-student.xml
middle-student.xml
test-student.xml
test-student.xml
mybatis-config-middle.xml
mybatis-config-middle.xml
mybatis-config-test.xml
mybatis-config-test.xml
StudentConst.java
StudentConst.java
DruidDataSourceFactory.java
DruidDataSourceFactory.java
Student.java(middle包下)
student.java
Student.java(test包下)
Student.java
MiddleProcess.java
MiddleProcess.java
TestProcess.java
TestProcess.java
MiddleProcessImpl.java
MiddleProcessImpl.java
TestProcessImpl.java
TestProcessImpl.java
Consumer.java
Consumer.java
Producer.java
Producer.java
Main.java
Main.java
SqlSessionUtil.java
SqlSessionUtil.java
sql脚本
middle.student
test.student
演示
部署如何启动指定main
修改pom.xml
mainClass指定启动包下的main
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <!-- 默认启动 程序,mainClass指定启动的main函数的报名 --> <mainClass>com.cyb.start.Main</mainClass> <layout>JAR</layout> <addResources>true</addResources> </configuration> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
修改log4j.properties日志输出目录