-
2021-03-05 17:06:15
标签:
由于最近工作遇到性能问题,尝试研究用多线程来实现,结果速度快了好几倍
下面是多线程查询的部分代码,提供给大家参考下:
线程类:
带返回值的类要实现Callable接口,具体业务逻辑没有实现,只是写了个空方法在里面
package com.sanfy.demo.thread;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import com.sanfy.demo.model.WorkLoad;
public class QueryThread implements Callable> {
private List workLoads;
private WorkLoadService workLoadService;
private Map parameters;
private List dates;
private int begin;
private int end;
public QueryThread(Map parameters,List dates, int begin, int end,WorkLoadService workLoadService) {
super();
this.parameters = parameters;
this.dates = dates;
this.begin = begin;
this.end = end;
this.workLoadService=workLoadService;
}
/**
* @return Map
* @see java.lang.Callable#call()
*/
@Override
public Map call() {
for (int i = begin; i < end; i++) {
queryListByMap(dates.get(i));
}
return workLoads;
}
/**
* 查询方法
*
* @param date 日期
* @param i i
*/
private void queryListByMap(String date) {
//具体的查询
workLoads=workLoadService.queryList(parameters,date);
}
public List getWorkLoads() {
return workLoads;
}
public void setWorkLoads(List workLoads) {
this.workLoads = workLoads;
}
public WorkLoadService getWorkLoadService() {
return workLoadService;
}
public void setWorkLoadService(WorkLoadService workLoadService) {
this.workLoadService = workLoadService;
}
public Map getParameters() {
return parameters;
}
public void setParameters(Map parameters) {
this.parameters = parameters;
}
public List getDates() {
return dates;
}
public void setDates(List dates) {
this.dates = dates;
}
public int getBegin() {
return begin;
}
public void setBegin(int begin) {
this.begin = begin;
}
public int getEnd() {
return end;
}
public void setEnd(int end) {
this.end = end;
}
}
serive调用线程类来实现多线程查询:
package com.sanfy.demo.thread;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import com.sanfy.demo.model.WorkLoad;
public class QuerySerivce {
private WorkLoadService workLoadService;
public List queryListsByMap(String sqlCode, Map parameters) throws Exception {
List workLoads=new ArrayList();
long startTime = System.currentTimeMillis();
List dates = DateUtil.getCurrentInYear(parameters.get("startDate").toString(), 12);
if (dates != null && dates.size() > 0) {
int len = dates.size();
// 创建线程的个数
int count = len % 2 == 0 ? len / 2: (len / 2) + 1;
// 创建线程池数量
ExecutorService pool = Executors.newFixedThreadPool(count);
List>> tasks = new ArrayList>>();
for (int i = 0; i < count; i++) {
// 每个线程负责插入数据的开始位置
int start = i * 2;
// 每个线程负责插入数据的结束位置
int end = i == count - 1 ? i * 2 + (len - i * 2) : i* 2+ 2;
Map parametersClone = new HashMap();
parametersClone.putAll(parameters);
// 线程返回值
Callable> task = new QueryThread(parametersClone, sqlCode, dates, start, end,workLoadService);
tasks.add(task);
}
// 执行线程
List>> futures = pool.invokeAll(tasks);
// 处理线程返回结果
if (futures != null && futures.size() > 0) {
for (Future> future : futures) {
workLoads.addAll(future.get());
}
}
// 关闭线程池
pool.shutdown();
}
long endTime = System.currentTimeMillis();
logger.info("多个情况报表查询总共花了:" + (endTime - startTime) / 1000 + "秒时间!");
return workLoads;
}
}
后续会补上多线程批量保存与android多线程支持断点续传下载的内容
由于是实际项目,所以把具体的内容删除了,直接修改的,供大家参考下
标签:
更多相关内容 -
java多线程视频教程(共七套)
2019-05-07 18:18:3001、【中级原理】java多线程并发编程入门原理精通视频教程 02、【中级原理专题】java并发编程多线程高级专题课程 03. 【中级原理】高并发编程原理和线程池精通教程 04、【高级原理】Java并发多线程编程基础原理与... -
java socket多线程文件传输实例项目
2016-08-10 11:04:25使用java socket开发的多线程文件上传下载的实例项目,多线程并发测试中可以支持200个,可能由于我电脑的配置问题,一般在并发大于200时client端可能会出现"阻塞"问题,还请大家指教 -
Java 多线程的应用场景
2022-05-01 09:49:54程序设计 多线程场景下需考虑线程安全的问题,避免多个线程争抢同一个资源导致业务逻辑出现错误。实现线程安全的方式有很多,这里使用Java Lock 接口中的方法实现。 代码示例 import java.util.concurrent.locks....业务需求
电影院新片首映,观影人数大量增加,为提高日营业额,线下售票窗口由原单窗口调整为3窗口,设计一段简单的程序模拟该售票过程。
程序设计
多线程场景下需考虑线程安全的问题,避免多个线程争抢同一个资源导致业务逻辑出现错误。实现线程安全的方式有很多,这里使用Java Lock 接口中的方法实现。
代码示例import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * 测试类 */ public class DemoTest { public static void main(String[] args) { //窗口01 new Thread(() -> { while (true) { //售票并获取售票后的当前票余量 int currentTickets = TicketSource.saleTickets(); //模拟售票员卖出一张票用时1秒 waitProcess(); //票已卖完 if (currentTickets <= 0) break; } }, "01").start(); //窗口02 new Thread(() -> { while (true) { int currentTickets = TicketSource.saleTickets(); waitProcess(); if (currentTickets <= 0) break; } }, "02").start(); //窗口03 new Thread(() -> { while (true) { int currentTickets = TicketSource.saleTickets(); waitProcess(); if (currentTickets <= 0) break; } }, "03").start(); } private static void waitProcess() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } /** * 电影票资源类 */ class TicketSource { //当前电影票余量 private static int currentTickets = 30; //加锁确保多线程场景下的线程安全 private static final Lock lock = new ReentrantLock(); /** * 卖票 * * @return 当前电影票余量 */ public static int saleTickets() { lock.lock(); try { if (currentTickets > 0) { //模拟卖票 currentTickets--; if (currentTickets == 0) { //票余量为 0 停止售卖 System.out.println( Thread.currentThread().getName() + "窗口出票成功!" + "当前票余量:" + currentTickets + " 今日票已卖完!"); } else { System.out.println( Thread.currentThread().getName() + "窗口出票成功!" + "当前票余量:" + currentTickets); } } else { //票余量为 0 停止售卖 System.out.println(Thread.currentThread().getName() + "窗口:今日票已卖完!"); } } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } return currentTickets; } }
运行结果
D:\installPath\Java\jdk1.8.0_121\bin\java.exe "-javaagent:D:\installPath\IntelliJ IDEA 2019.1.4\lib\idea_rt.jar=64339:D:\installPath\IntelliJ IDEA 2019.1.4\bin" 01窗口出票成功!当前票余量:29 02窗口出票成功!当前票余量:28 03窗口出票成功!当前票余量:27 01窗口出票成功!当前票余量:26 03窗口出票成功!当前票余量:25 02窗口出票成功!当前票余量:24 03窗口出票成功!当前票余量:23 01窗口出票成功!当前票余量:22 02窗口出票成功!当前票余量:21 02窗口出票成功!当前票余量:20 03窗口出票成功!当前票余量:19 01窗口出票成功!当前票余量:18 01窗口出票成功!当前票余量:17 02窗口出票成功!当前票余量:16 03窗口出票成功!当前票余量:15 02窗口出票成功!当前票余量:14 01窗口出票成功!当前票余量:13 03窗口出票成功!当前票余量:12 01窗口出票成功!当前票余量:11 03窗口出票成功!当前票余量:10 02窗口出票成功!当前票余量:9 03窗口出票成功!当前票余量:8 02窗口出票成功!当前票余量:7 01窗口出票成功!当前票余量:6 03窗口出票成功!当前票余量:5 02窗口出票成功!当前票余量:4 01窗口出票成功!当前票余量:3 01窗口出票成功!当前票余量:2 03窗口出票成功!当前票余量:1 02窗口出票成功!当前票余量:0 今日票已卖完! 01窗口:今日票已卖完! 03窗口:今日票已卖完! Process finished with exit code 0
-
多线程在Java项目中的使用案例(笔记)
2022-04-21 14:34:19多线程在Java项目中的使用案例(笔记) 实现runnable接口 @Override public Boolean addMeetingExpertIds(MeetAddExpertDto meetAddExpertDto, Long userId) { // 会议关联到专家 // 如果需要发给专家 new ...多线程在Java项目中的使用案例(笔记)
- 实现runnable接口
@Override public Boolean addMeetingExpertIds(MeetAddExpertDto meetAddExpertDto, Long userId) { // 会议关联到专家 // 如果需要发给专家 new Thread(new Runnable() { @Override public void run() { try { if (meetAddExpertDto.getExpertIdList().size() > 0) { // 准备通知数据 List<AddMeeTempDto> addMeeTempDtoList = new ArrayList<>(); for (Long expertId : meetAddExpertDto.getExpertIdList()) { AddMeeTempDto addMeeTempDto = new AddMeeTempDto() .setExpertId(expertId) .setTemplateDetail(meetAddExpertDto.getTemplate() + expertId); addMeeTempDtoList.add(addMeeTempDto); } expertMeetingService.addExpertMeetingBatch(meetAddExpertDto.getMeetingId(), addMeeTempDtoList, userId); Thread.sleep(300); } } catch (Exception e) { e.printStackTrace(); } finally { log.info("线程结束"); } } }).start(); return true; }
Lambda写法
@Override public void test(Integer meetId) { new Thread(() -> { try { for (int i = 0; i < 10; i++) { log.info("线程" + meetId + "正在输出" + i);//逻辑代码 if (i == 5) { Thread.sleep(200); log.info("线程" + meetId + "睡眠"); } } } catch (InterruptedException e) { e.printStackTrace(); } finally { log.info("线程结束"); } }).start(); }
- 创建线程池
@Override public void test(Integer meetId) { //创建一个具有10个线程的线程池 ExecutorService threadPool = Executors.newFixedThreadPool(10); long threadpoolUseTime = System.currentTimeMillis(); threadPool.execute(new Runnable() { @Override public void run() { try { for (int i = 0; i < 10; i++) { log.info("线程" + meetId + "正在输出" + i); if (i == 5) { Thread.sleep(200); log.info("线程" + meetId + "睡眠"); } } } catch (InterruptedException e) { e.printStackTrace(); } } }); long threadpoolUseTime1 = System.currentTimeMillis(); System.out.println("多线程用时"+(threadpoolUseTime1-threadpoolUseTime)); //销毁线程池 threadPool.shutdown(); }
Lambda写法
@Override public void test(Integer meetId) { //创建一个具有10个线程的线程池 ExecutorService threadPool = Executors.newFixedThreadPool(10); long threadpoolUseTime = System.currentTimeMillis(); threadPool.execute(new Runnable() { @Override public void run() { try { for (int i = 0; i < 10; i++) { log.info("线程" + meetId + "正在输出" + i); if (i == 5) { Thread.sleep(200); log.info("线程" + meetId + "睡眠"); } } } catch (InterruptedException e) { e.printStackTrace(); } } }); long threadpoolUseTime1 = System.currentTimeMillis(); System.out.println("多线程用时"+(threadpoolUseTime1-threadpoolUseTime)); //销毁线程池 threadPool.shutdown(); }
-
项目中Java的多线程一般用在哪些场景?
2021-04-02 13:36:20项目中Java的多线程一般用在哪些场景?多线程使用的主要目的在于举个简单的例子伪代码多线程的常见应用场景 多线程使用的主要目的在于 1、吞吐量:你做WEB,容器帮你做了多线程,但是他只能帮你做请求层面的。简单...项目中Java的多线程一般用在哪些场景?
多线程使用的主要目的在于
1、吞吐量:你做WEB,容器帮你做了多线程,但是他只能帮你做请求层面的。简单的说,可能就是一个请求一个线程。或多个请求一个线程。如果是单线程,那同时只能处理一个用户的请求。
2、伸缩性:也就是说,你可以通过增加CPU核数来提升性能。如果是单线程,那程序执行到死也就利用了单核,肯定没办法通过增加CPU核数来提升性能。
鉴于是做WEB的,第1点可能你几乎不涉及。那这里我就讲第二点吧。
举个简单的例子
假设有个请求,这个请求服务端的处理需要执行3个很缓慢的IO操作(比如数据库查询或文件查询),那么正常的顺序可能是(括号里面代表执行时间):
- 读取文件1 (10ms)
- 处理1的数据(1ms)
- 读取文件2 (10ms)
- 处理2的数据(1ms)
- 读取文件3 (10ms)
- 处理3的数据(1ms)
- 整合1、2、3的数据结果 (1ms)
单线程总共就需要34ms。
那如果你在这个请求内,把ab、cd、ef分别分给3个线程去做,就只需要12ms了。
所以多线程不是没怎么用,而是,你平常要善于发现一些可优化的点。然后评估方案是否应该使用。假设还是上面那个相同的问题:但是每个步骤的执行时间不一样了。
- 读取文件1 (1ms)
- 处理1的数据(1ms)
- 读取文件2 (1ms)
- 处理2的数据(1ms)
- 读取文件3 (28ms)
- 处理3的数据(1ms)
- 整合1、2、3的数据结果 (1ms)
单线程总共就需要34ms。
如果还是按上面的划分方案(上面方案和木桶原理一样,耗时取决于最慢的那个线程的执行速度),在这个例子中是第三个线程,执行29ms。那么最后这个请求耗时是30ms。比起不用单线程,就节省了4ms。但是有可能线程调度切换也要花费个1、2ms。因此,这个方案显得优势就不明显了,还带来程序复杂度提升。不太值得。
那么现在优化的点,就不是第一个例子那样的任务分割多线程完成。而是优化文件3的读取速度。可能是采用缓存和减少一些重复读取。
首先,假设有一种情况,所有用户都请求这个请求,那其实相当于所有用户都需要读取文件3。那你想想,100个人进行了这个请求,相当于你花在读取这个文件上的时间就是28×100=2800ms了。那么,如果你把文件缓存起来,那只要第一个用户的请求读取了,第二个用户不需要读取了,从内存取是很快速的,可能1ms都不到。
伪代码
public class MyServlet extends Servlet{ private static Map<String, String> fileName2Data = new HashMap<String, String>(); private void processFile3(String fName){ String data = fileName2Data.get(fName); if(data==null){ data = readFromFile(fName); //耗时28ms fileName2Data.put(fName, data); } //process with data } }
看起来好像还不错,建立一个文件名和文件数据的映射。如果读取一个
map
中已经存在的数据,那么就不不用读取文件了。可是问题在于,Servlet是并发,上面会导致一个很严重的问题,死循环。因为,HashMap在并发修改的时候,可能是导致循环链表的构成!!!(具体你可以自行阅读HashMap源码)如果你没接触过多线程,可能到时候发现服务器没请求也巨卡,也不知道什么情况!
好的,那就用ConcurrentHashMap,正如他的名字一样,他是一个线程安全的HashMap,这样能轻松解决问题。
public class MyServlet extends Servlet{ private static ConcurrentHashMap<String, String> fileName2Data = new ConcurrentHashMap<String, String>(); private void processFile3(String fName){ String data = fileName2Data.get(fName); if(data==null){ data = readFromFile(fName); //耗时28ms fileName2Data.put(fName, data); } //process with data } }
这样真的解决问题了吗,这样虽然只要有用户访问过文件a,那另一个用户想访问文件a,也会从fileName2Data中拿数据,然后也不会引起死循环。
可是,如果你觉得这样就已经完了,那你把多线程也想的太简单了,骚年!你会发现,1000个用户首次访问同一个文件的时候,居然读取了1000次文件(这是最极端的,可能只有几百)。What the fuckin hell!!!
难道代码错了吗,难道我就这样过我的一生!
好好分析下。Servlet是多线程的,那么
public class MyServlet extends Servlet{ private static ConcurrentHashMap<String, String> fileName2Data = new ConcurrentHashMap<String, String>(); private void processFile3(String fName){ String data = fileName2Data.get(fName); //“偶然”-- 1000个线程同时到这里,同时发现data为null if(data==null){ data = readFromFile(fName); //耗时28ms fileName2Data.put(fName, data); } //process with data } }
上面注释的“偶然”,这是完全有可能的,因此,这样做还是有问题。
因此,可以自己简单的封装一个任务来处理。
public class MyServlet extends Servlet{ private static ConcurrentHashMap<String, FutureTask> fileName2Data = new ConcurrentHashMap<String, FutureTask>(); private static ExecutorService exec = Executors.newCacheThreadPool(); private void processFile3(String fName){ FutureTask data = fileName2Data.get(fName); //“偶然”-- 1000个线程同时到这里,同时发现data为null if(data==null){ data = newFutureTask(fName); FutureTask old = fileName2Data.putIfAbsent(fName, data); if(old==null){ data = old; }else{ exec.execute(data); } } String d = data.get(); //process with data } private FutureTask newFutureTask(final String file){ return new FutureTask(new Callable<String>(){ public String call(){ return readFromFile(file); } private String readFromFile(String file){return "";} } } }
以上所有代码可以直接运行。
多线程最多的场景:web服务器本身;各种专用服务器(如游戏服务器);
多线程的常见应用场景
- 后台任务,例如:定时向大量(100w以上)的用户发送邮件;
- 异步处理,例如:发微博、记录日志等;
- 分布式计算
-
java多线程执行任务(工具)
2022-03-09 14:54:09在项目开发的过程中经常会碰到多线程执行任务,每次用线程池实现时,由于每次的需求都有所差别有时是所有任务同时执行有时是分批次执行有时还需要知道所有任务什么时候执行完。今天闲着写了一个通用的多线程执行工具... -
Java多线程(超详细!)
2021-05-12 17:00:59注意:一个进程可以启动多个线程。 eg.对于java程序来说,当在DOS命令窗口中输入: java HelloWorld 回车之后。 会先启动JVM,而JVM就是一个进程。 JVM再启动一个主线程调用main方法。 同时再启动一个垃圾回收线程... -
面试官:说说项目中 Java 多线程一般都用于哪些应用场景?
2021-03-14 00:49:22>>号外:关注“Java精选”公众号,菜单栏->聚合->干货分享,回复关键词领取视频资料、开源项目。多线程使用的主要目的在于:1、吞吐量:你做WEB,容器帮你做了多... -
Java多线程【三种方法】
2022-03-21 15:40:01对比:并发是指:在同一个时间段内,两个或多个程序执行,有时间上的重叠(宏观上是同时,微观上仍是顺序执行) 进程与线程 进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度... -
Java多线程超详细教程(1)
2021-02-28 14:03:17java中的多线程是一个同时执行多个线程的过程。线程基本上是一个轻量级的子进程,是最小的处理单元。...Java多线程主要用于游戏,动画等Java多线程的优点1)它不会阻止用户,因为线程是独立的,您可以同时执行多个操... -
java多线程-学习总结(完整版)
2020-12-04 00:02:16java多线程 线程和进程 线程的生命周期 新建New 就绪&运行 Runable&Running 阻塞Blocked 等待 waiting 计时等待Time waiting 销毁Terminated 线程池概念和多线程使用场景 线程池的参数解析 线程池阻塞队列... -
Java多线程经典案例分享
2021-12-28 21:00:41实现一个容器,提供两个方法,add(),count() 写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数,当个数到5个时,线程2给出提示并结束。 本案例我通过闭锁(也叫门栓锁)实现,实现如下: package ... -
java多线程编程实例
2018-05-25 10:01:22这篇文章主要介绍了java多线程编程实例,分享了几则多线程的实例代码,具有一定参考价值,加深多线程编程的理解还是很有帮助的,需要的朋友可以参考下。1.相关知识:Java多线程程序设计到的知识:(1)对同一个数量... -
java多线程(一)、java Web项目中什么场景中会用到java多线程?
2018-12-26 09:34:10参考文档: https://blog.csdn.net/u012661010/article/details/76696309 -
Java多线程数据库事务提交控制
2021-08-28 20:43:44三、尝试多线程进行数据修改 四、为多线程提交添加事务控制 总结 前言 公司业务中遇到一个需求,需要同时修改最多约5万条数据,而且还不支持批量或异步修改操作。循环操作耗时太长,为提高效率我进行了下面... -
Java多线程开发的实际应用场景
2021-07-28 18:01:05SaaS型微服务脚手架,具备用户管理、资源权限管理、网关统一鉴权、Xss防跨站攻击、自动代码生成、多存储系统、分布式事务、分布式定时任务等多个模块,支持多业务系统并行开发, 支持多服务并行开发,可以作为后端... -
深度讲解Java多线程开发—电子表项目实现
2020-07-23 16:09:08今天和大家分享一个使用Java多线程开发的电子表项目,可以实现电子表中时间的实时显示,修改以及秒表的功能。 Java电子表设计的设计顺序为从前端界面到后端类及线程的设计,之后将前后端相结合而成。以下是电子表的... -
java多线程小程序实例
2008-12-15 13:00:42java多线程小程序实例 java多线程小程序实例 -
面试官:公司项目中Java的多线程一般用在哪些场景?
2021-02-21 09:45:00来源:cnblogs.com/kenshinobiy/p/4671314.html多线程使用的主要目的在于:1、吞吐量:你做WEB,容器帮你做了多线程,但是他只能帮你做请求层面的。简单的说... -
【java多线程编程】三种多线程的实现方式
2019-01-01 16:20:56文章目录前言进程与线程继承Thread类,实现多线程FAQ 为什么多线程的启动不直接使用run()方法而必须使用Thread类中start()方法呢?...在java语言最大的特点是支持多线程的开发(也是为数不多... -
2021版Java多线程教程
2021-04-04 10:06:58 -
java socket多线程文件上传下载实例项目
2013-10-12 11:36:18本项目为我现在负责开发的一个项目,在多线程并发测试中可以支持200个,可能由于我电脑的配置问题,一般在并发大于200时client端可能会出现"阻塞"问题,还请大家指教 -
Java多线程中static变量的使用
2022-02-17 21:44:22Java多线程中static变量的使用 线程,是我们项目中绕不过的重点领域。提到线程,就常会听到线程安全的术语。那什么是线程安全呢?通俗点说,就是线程访问时不产生资源冲突。其实,这是一个有点难以 -
Java获得多线程的返回结果方式
2020-06-15 14:47:35一:Java创建线程方式 继承Thread类或者实现Runnable接口。 但是Runnable 的 run() 方法是不带返回值的,那如果我们需要一个耗时任务在执行完之后给予返回值,应该怎么做呢? 第一种方法:在 Runnable 的实现类中... -
Java多线程并行处理任务的实现
2019-04-20 21:08:02Java多线程并行处理任务的实现 在实际项目开发的过程中,遇到过需要处理一个由多个子任务组成的任务的问题.顺序处理起来会造成响应时间超长,用户体验不好的问题.我想到一个解决方案,即使用多线程并行处理子任务.思路... -
多线程处理大量数据 java
2020-09-27 17:12:11项目场景: 简述项目相关背景: 例如:获取大量数据并处理,生成execl文件导出 问题描述: 5W条数据处理后生成execl文件需要6个小时,效率慢 APP 中接收数据代码: @Override public void run() { bytes = ... -
JAVA多线程基础篇 8、线程隔离与ThreadLocal
2022-03-14 15:23:05ThreadLocal的设计是为了能够在当前线程中有属于自己的变量,其原理是每个线程内部其实都存储了一个ThreadLocalMap来记录保存。ThreadLocal对象存在内存泄露的风险,需要手工remove。 -
Java开发中多线程与高并发需要注意的 15 个细节
2022-03-29 10:22:44执行时间长或线程数量多用系统锁(-OS),时间短(指加锁代码时间短)用自旋锁(但是线程不能太多)。 锁定的对象不能是String常量 Integer(特殊处理过) Long等基本数据类型,比如String在常量池中,相同字面量是... -
JAVA - 大批量集合数据多线程处理
2021-04-25 17:28:53大批量数据多线程处理 原因:最近写项目多时候遇到一个这样的问题,Excel批量导入表格,数据量较大,处理时间较长。然后小编想到了可以用多线程处理。 例: //导入失败集合 List<BatchPayDetail...