一.使用Callable多线程:
通过Callable接口实现多线程
实现Callable重写call方法;
实现Callable和实现Runnable类似,但是功能更强大,具体表现在:
a.可以在任务结束后提供一个返回值,Runnable不行;
b.call方法可以抛出异常,Runnable的run方法不行;
c.可以通过运行Callable得到的Fulture对象监听目标线程调用call方法的结果,得到返回值,(fulture.get(),调用后会阻塞,直到获取到返回值);
1、Callable接口介绍:
(1)java.util.concurrent.Callable是一个泛型接口,只有一个call()方法;
(2)call()方法抛出异常Exception异常,且返回一个指定的泛型类对象;
2、Callable接口实现多线程的应用场景
(1)当父线程想要获取子线程的运行结果时;
3、使用Callable接口实现多线程的步骤
(1)第一步:创建Callable子类的实例化对象;
(2)第二步:创建FutureTask对象,并将Callable对象传入FutureTask的构造方法中(注意:FutureTask实现了Runnable接口和Future接口);
(3)第三步:实例化Thread对象,并在构造方法中传入FurureTask对象;
(4)第四步:启动线程;
二.需求:
做一个报表功能:在一个方法中查询多个数据库表的结果,然后汇总返回;
由于单独查询一个数据库表速度较慢(大字段查询),此时如果串行查询多个表的话效率会非常低,所以需要多线程同时查询数据库,等全部查询完毕后再汇总!
DAO层:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | package com.moerlong.yj.mapper; import org.apache.ibatis.annotations.Select; import org.springframework.stereotype.Repository; @Repository public interface BaseMapper { @Select ( "select hf_industry_record from msd_hf_industry_person_srcdata where card_id = #{cardId}" ) public String selectIndustryData(String cardId); @Select ( "select hf_judicial_record from msd_hf_judicial_person_srcdata where card_id = #{cardId}" ) public String selectJudicialData(String cardId); @Select ( "select td_record from msd_td_preloan_srcdata where card_id = #{cardId}" ) public String selectTdData(String cardId);<br>} |
Service层:
串行执行:
package com.moerlong.yj.Service; import com.moerlong.yj.mapper.BaseMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class TestNoCallable { @Autowired private BaseMapper baseMapper; public String test1(String cardId) throws Exception{ long start = System.currentTimeMillis(); //三个串行查询 String industryData = baseMapper.selectIndustryData(cardId); String judicialData = baseMapper.selectJudicialData(cardId); String tdData = baseMapper.selectTdData(cardId); Thread.sleep(3000); //此处模拟每个查询添加1s耗时 String result = industryData + judicialData + tdData; long end = System.currentTimeMillis(); System.out.println("串行执行:" + (end-start)); return result; } }
并行执行:
package com.moerlong.yj.Service; import com.moerlong.yj.mapper.BaseMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.lang.reflect.Method; import java.util.concurrent.*; @Service public class TestCallable { @Autowired private BaseMapper baseMapper; public String test2(String cardId) throws Exception{ // 三个线程的线程池,核心线程=最大线程,没有临时线程,阻塞队列无界 ExecutorService executorService = Executors.newFixedThreadPool(3); long start = System.currentTimeMillis(); // 开启线程执行 // 注意,此处Future对象接收线程执行结果不会阻塞,只有future.get()时候才会阻塞(直到线程执行完返回结果) Future future1 = executorService.submit(new SelectTask<>(this, "selectIndustryData", new Object[]{cardId})); Future future2 = executorService.submit(new SelectTask<>(this, "selectJudicialData", new Object[]{cardId})); Future future3 = executorService.submit(new SelectTask<>(this, "selectTdData", new Object[]{cardId})); //此处用循环保证三个线程执行完毕,再去拼接三个结果 do{ System.out.println("多任务同时执行中..."); }while (!(future1.isDone() && future2.isDone() && future3.isDone())); String result = (String)future1.get() + future2.get() + future3.get(); long end = System.currentTimeMillis(); System.out.println("并行执行:" + (end-start)); return result; } //下面是三个真正执行任务(查数据库)的方法 public String selectIndustryData(String cardId) throws Exception{ String result = baseMapper.selectIndustryData(cardId); Thread.sleep(1000); //模拟添加1s耗时 return result; } public String selectJudicialData(String cardId) throws Exception{ String result = baseMapper.selectJudicialData(cardId); Thread.sleep(1000); return result; } public String selectTdData(String cardId) throws Exception{ String result = baseMapper.selectTdData(cardId); Thread.sleep(1000); return result; } //任务线程类 class SelectTask<T> implements Callable<T> { private Object object; private Object[] args; private String methodName; public SelectTask(Object object, String methodName, Object[] args) { this.object = object; this.args = args; this.methodName = methodName; } @Override public T call() throws Exception { Method method = object.getClass().getMethod(methodName,String.class); //此处应用反射机制,String.class是根据实际方法参数设置的 return (T) method.invoke(object, args); } } }
控制层:
package com.moerlong.yj.controller; import com.moerlong.yj.Service.TestCallable; import com.moerlong.yj.Service.TestNoCallable; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class TestController {
@Autowired private TestNoCallable testNoCallable; @Autowired private TestCallable testCallable; @RequestMapping(value = "/test1/{cardId}") public String test1(@PathVariable String cardId) throws Exception{ String result = testNoCallable.test1(cardId); return result; } @RequestMapping(value = "/test2/{cardId}") public String test2(@PathVariable String cardId) throws Exception{ String result = testCallable.test2(cardId); return result; } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix