目录
首先简单区分程序、进程、线程
线程池
概念
为什么要用线程池
实现
实体类
controller
service
serviceImp
mapper
xml
工具类——线程合并
工具类——每个线程查询数据
工具类——获取Bean
测试
首先简单区分程序、进程、线程
- 程序(program):为完成特定任务、用某种语言编写的一组指令的集合。
- 进程(process):程序的一次执行过程,或正在运行的一个程序。
- 线程(thread):是一个程序内部的一条执行路径,有时一条线程可称为轻量进程。
举个例子:某人想盖房,叫来了施工队(程序),商量好之后,施工队开始盖房(进程),期间李四砌墙(线程),王五搬砖(线程),李四和王五干活是同时进行的(多线程)。
线程池
概念
线程池顾名思义就是事先创建若干个可执行的线程放入一个池(容器)中,需要的时候从池中获取线程不用自行创建,使用完毕不需要销毁线程而是放回池中。
为什么要用线程池
在java中,创建一个对象要获取内存资源或者其它更多资源,创建和销毁对象太费时间。而且虚拟机会跟踪每一个对象,以便能够在对象销毁后进行垃圾回收。所以提高服务程序效率的一个手段就是尽可能减少创建和销毁对象的次数,特别是一些很耗资源的对象创建和销毁,使用线程池可以有效减少创建和销毁线程对象的开销。
实现
实现多线程有4中方式,本文使用Future + Callable完成多线程查询。
当查询操作会查出大量数据时,使用单线程会让数据的接收变得极慢,这时使用多线程,将查询数据根据cpu核心数进行分割,每个线程查询自己对应的数据区域。通过线程池完成多线程查询并把查询到的数据合并,完成查询。
实体类
@Data
@TableName("admin")
public class Blog implements Serializable {
private Integer id;
private String account;
private String password;
}
controller
@RestController
@RequestMapping("/thread")
public class ThreadController {
@Resource
private IBlogService blogService;
@GetMapping("/testThredData")
public List testThredData(){
return blogService.getAllResult();
}
}
service
public interface IBlogService extends IService<Blog> {
//每个线程分页查询
public List<Blog> getQueryData(Integer start, Integer end);
//合并线程结果
public List getAllResult();
}
serviceImp
@Service
public class BlogServiceImpl extends ServiceImpl<BlogMapper, Blog> implements IBlogService {
@Resource
private BlogMapper blogMapper;
@Autowired
private MultiThreadQueryUtil multiThreadQueryUtil;
//每个线程的查询业务
@Override
public List<Blog> getQueryData(Integer start, Integer end) {
return this.blogMapper.getQueryData(start,end);
}
@Override
public List getAllResult() {
//需要查询的总条数
int count = blogMapper.getCount();
//合并多线程
return multiThreadQueryUtil.getMultiCombineResult(count);
}
}
mapper
public interface BlogMapper extends BaseMapper<Blog> {
//每个线程查询的数据
List<Blog> getQueryData(@Param("start") int start, @Param("end") int end);
//需要查询的总条数
int getCount();
}
xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.hmdp.mapper.BlogMapper">
<select id="getQueryData" resultType="com.hmdp.entity.Blog">
select * from admin limit #{start},#{end}
</select>
<select id="getCount" resultType="java.lang.Integer">
select count(id) from admin
</select>
</mapper>
工具类——线程合并
@Service
public class MultiThreadQueryUtil {
/**
* 获取多线程结果并进行结果合并
* @return
*/
public List<List> getMultiCombineResult(int count) {
//多线程查询开始时间
long start = System.currentTimeMillis();
//用于存放合并后最终的查询数据
List<List> result = new ArrayList<>();
//定义固定长度的线程池,防止浪费资源,5条就够用
ExecutorService executorService = Executors.newFixedThreadPool(5);
//Callable用于产生结果
List<Callable<List>> tasks = new ArrayList<>();
//根据数据总条数与线程数分配任务
for (int i = 0; i < 5; i++) {
//每个线程需要查询数据的数量,向上取整
int endNum = (int) Math.ceil((double)count/5);
//每个线程查询数据开始的位置=线程次序 * endNum
int startNum =i * endNum;
Callable<List> qfe = new ThredQuery(startNum, endNum);
tasks.add(qfe);
}
try{
//通过Future异步获得线程的结果,不用等一条线程执行完再执行下一条线程
List<Future<List>> futures=executorService.invokeAll(tasks);
//处理线程返回结果
if(futures!=null&&futures.size() > 0){
for (Future<List> future:futures){
result.addAll(future.get());
}
}
//关闭线程池,回收资源
executorService.shutdown();
}catch (Exception e){
e.printStackTrace();
}
//多线程查询结束时间
long end = System.currentTimeMillis();
System.out.println("线程查询数据用时:"+(end-start)+"ms");
return result;
}
}
工具类——每个线程查询数据
public class ThredQuery implements Callable<List> {
//引入获取Bean的工具类
public static SpringContextUtil springContextUtil = new SpringContextUtil();
private int start;
private int end;
//每个线程查询出来的数据集合
private List datas;
public ThredQuery(int start,int end) {
this.start=start;
this.end=end;
//每个线程查询出来的数据集合
//配置类不受Spring管理,通过类名手动获取Bean调用线程查询的业务
IBlogService blogService = springContextUtil.getBean(IBlogService.class);
List count = blogService.getQueryData(start,end);
datas = count;
}
//返回数据给Future
@Override
public List call() throws Exception {
return datas;
}
}
工具类——获取Bean
@Component
public class SpringContextUtil implements ApplicationContextAware {
/**
* 上下文对象实例
*/
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
/**
* 获取applicationContext
*
* @return
*/
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
/**
* 获取HttpServletRequest
*/
public static HttpServletRequest getHttpServletRequest() {
return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
}
public static String getDomain(){
HttpServletRequest request = getHttpServletRequest();
StringBuffer url = request.getRequestURL();
return url.delete(url.length() - request.getRequestURI().length(), url.length()).toString();
}
public static String getOrigin(){
HttpServletRequest request = getHttpServletRequest();
return request.getHeader("Origin");
}
/**
* 通过name获取 Bean.
*
* @param name
* @return
*/
public static Object getBean(String name) {
return getApplicationContext().getBean(name);
}
/**
* 通过class获取Bean.
*
* @param clazz
* @param <T>
* @return
*/
public static <T> T getBean(Class<T> clazz) {
return getApplicationContext().getBean(clazz);
}
/**
* 通过name,以及Clazz返回指定的Bean
*
* @param name
* @param clazz
* @param <T>
* @return
*/
public static <T> T getBean(String name, Class<T> clazz) {
return getApplicationContext().getBean(name, clazz);
}
}
测试
8条数据全部获取,查询用时8ms