陌陌笔试--并发打印文件内最有钱的老板的消费金额(算法)

题目:

算法中需要打印消费前十老板的消费金额,解决保留两位小数,并发是 JAVA 中的常考题,
在这里插入图片描述

我这里简单模拟下了数据,关键数据是用户id和消费金额。

解题思路:

1. 最简单的思路是单线程,偷懒(dog),

public class Main {public static void main(String[] args) throws FileNotFoundException {Scanner scanner = new Scanner(new File("C:\\Users\\15031\\Desktop\\momo\\momo\\src\\data.txt"));System.out.println(scanner.nextLine());HashMap<Integer,Double>map = new HashMap<>();String s = scanner.nextLine();while (scanner.hasNextLine()) {String line = scanner.nextLine();String[] partLine = line.split(" ");int userId = Integer.parseInt(partLine[1]);double order = Double.parseDouble(partLine[2]);map.put(userId,map.getOrDefault(userId,0d) + order);}Queue<Map.Entry<Integer,Double>>queue = new PriorityQueue<>((entry0,entry1)->Double.compare(entry0.getValue(),entry1.getValue()));for(Map.Entry<Integer,Double> e : map.entrySet()){if(queue.size() < 10){queue.offer(e);}else {queue.poll();queue.offer(e);}}for(Map.Entry<Integer,Double> e : queue){System.out.println(e.getKey()+","+String.format("%.2f", e.getValue()));}}
}

在这里插入图片描述

我们可以使用文件流读取数据偷懒,然后再使用hashmap统计出现的次数,然后再使用优先队列统计前k个字符串,然后输出。

2. 多线程解决思路

多线程实际上要复杂的多,要考虑一个线程处理多少数据合适,当然,这是最笨的方法,甚至还要考虑是否要读入到内存里,如果数据较多,可能考虑内存占用问题。简单数据还好,可能几十万行也就占用几百兆内存。

在这里插入图片描述

  1. 内存缓冲器区

我们使用BlockingQueue,该队列是生产者消费者模型中常用的类

        BlockingQueue<String> queue = new LinkedBlockingQueue<>();
  1. 这里我们使用一个线程做生产者,多线程不知道怎么生产(dog),BlockingQueue做缓冲区。

生产者,生产者文件流如果使用多线程读取会比较复杂,故我们使用单线程,还有一个问题是读一行感觉消费处理起来也并不太高。(当然这里就先偷懒了)

producerExecutor.submit(() -> {try (Scanner scanner = new Scanner(new File("C:\\Users\\15031\\Desktop\\momo\\momo\\src\\data.txt"))) {while (scanner.hasNextLine()) {queue.put(scanner.nextLine());}for (int j = 0; j < CONSUMER_COUNT; j++) {queue.put("EOF"); // End-of-file markers for consumer threads}} catch (FileNotFoundException | InterruptedException e) {e.printStackTrace();}});
  1. 消费者处理

消费者这里我们直接使用线程池处理了,需要注意的是线程数并非越多越好,最好和计算机 cpu 核心数有相关性,同时这里一行数据切换一个线程也并不能提升效率(dog)

for (int i = 0; i < CONSUMER_COUNT; i++) {consumerExecutor.submit(() -> {try {while (true) {List<String> batch = queue.take();if (batch.contains("EOF")) {queue.put(Collections.singletonList("EOF")); // Pass the marker to other consumersbreak;}for (String line : batch) {String[] partLine = line.split(" ");int userId = Integer.parseInt(partLine[1]);double order = Double.parseDouble(partLine[2]);map.merge(userId, order, Double::sum);}}} catch (InterruptedException e) {e.printStackTrace();}});}

为什么要添加消费者数量的EOF 而不是先peek判断,然后再取出呢?

  • 线程安全:在使用 peek() 和 poll() 时,需要确保操作的原子性。使用两步操作会强行把原子操作变成不原子的,在操作之间peek()的数据可能被修改,造成线程安全问题。
  • 简化实现:尽可能简化并发模型可以减少复杂性和错误的可能性。直接使用 take() 可以确保每个元素只被处理一次,逻辑更为简单。
  1. 总体代码
```java
public class Main {private static final int PRODUCER_COUNT = 1; // 生产者threadsprivate static final int CONSUMER_COUNT = 4; // 消费者threadsprivate static final int TOP_N = 10; // Top N users by order amountpublic static void main(String[] args) throws FileNotFoundException {BlockingQueue<String> queue = new LinkedBlockingQueue<>();ConcurrentHashMap<Integer, Double> map = new ConcurrentHashMap<>();ExecutorService producerExecutor = Executors.newFixedThreadPool(PRODUCER_COUNT);ExecutorService consumerExecutor = Executors.newFixedThreadPool(CONSUMER_COUNT);// Producer threadsfor (int i = 0; i < PRODUCER_COUNT; i++) {producerExecutor.submit(() -> {try (Scanner scanner = new Scanner(new File("C:\\Users\\15031\\Desktop\\momo\\momo\\src\\data.txt"))) {while (scanner.hasNextLine()) {queue.put(scanner.nextLine());}for (int j = 0; j < CONSUMER_COUNT; j++) {queue.put("EOF"); // End-of-file markers for consumer threads}} catch (FileNotFoundException | InterruptedException e) {e.printStackTrace();}});}// Consumer threadsfor (int i = 0; i < CONSUMER_COUNT; i++) {consumerExecutor.submit(() -> {try {while (true) {String line = queue.take();if ("EOF".equals(line)) {queue.put("EOF"); // Pass the marker to other consumersbreak;}String[] partLine = line.split(" ");int userId = Integer.parseInt(partLine[1]);double order = Double.parseDouble(partLine[2]);map.merge(userId, order, Double::sum);}} catch (InterruptedException e) {e.printStackTrace();}});}producerExecutor.shutdown();consumerExecutor.shutdown();try {producerExecutor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);consumerExecutor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);} catch (InterruptedException e) {e.printStackTrace();}// Process the map to find top N usersPriorityQueue<Map.Entry<Integer, Double>> queueTopN = new PriorityQueue<>(Map.Entry.comparingByValue());for (Map.Entry<Integer, Double> entry : map.entrySet()) {if (queueTopN.size() < TOP_N) {queueTopN.offer(entry);} else if (entry.getValue() > queueTopN.peek().getValue()) {queueTopN.poll();queueTopN.offer(entry);}}List<Map.Entry<Integer, Double>> topNList = new ArrayList<>(queueTopN);topNList.sort((e0,e1)-> (int) (e0.getValue()-e1.getValue()));for (Map.Entry<Integer, Double> entry : topNList) {System.out.println(entry.getKey() + "," + String.format("%.2f", entry.getValue()));}}
}

思考:

😄 这里主要是对生产者和消费者模式的一个总结和复习,这些处理模式可能平时没感觉怎么用,但是一结合实际,我们就立马可以感受到这些处理的模式的优异之处了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/36967.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

狂神说Java之 rabbitmq高级分布式事务

分布式事务的完整架构图 案例场景分析 案例一&#xff1a;用RestTemplate演示&#xff08;不可靠生产&#xff0c;会出现问题&#xff09; 创建一个订单模块 创建一个OrderDataBaseService服务 创建一个order的service服务&#xff0c;调用saveOrder()方法 创建一个运单模块…

软件设计流程和开发流程及规范(Word)

2 过程总体描述 2.1 过程概述 2.2 过程流程图 3 过程元素描述 3.1 产品方案 3.2 产品设计 3.3 产品实现 获取方式&#xff1a;本文末个人名片直接获取。 软件资料清单列表部分文档清单&#xff1a;工作安排任务书&#xff0c;可行性分析报告&#xff0c;立项申请审批表&#x…

找不到vcomp140.dll怎么办,总结多种解决方法

​在日常使用电脑的过程中&#xff0c;我们可能会遇到一些错误提示&#xff0c;其中之一就是“vcomp140.dll丢失”。那么&#xff0c;vcomp140.dll是什么&#xff1f;它为什么会丢失&#xff1f;丢失后对电脑有什么影响&#xff1f;又该如何解决呢&#xff1f;本文将详细介绍vc…

根据肥胖类型选择减调方向收获窈窕身材

我们生活中胖子很多&#xff0c;从胖到瘦的人也不少&#xff0c;但瘦了后对自己身材满意的人却是不多的&#xff0c;很多人瘦了也只是减掉了身上的赘肉而已&#xff0c;大体的身形却是没有变化的&#xff0c;因此&#xff0c;并不感到满意。因为他们本身的形体是固定的&#xf…

SpringBoot-SpringBoot整合Swagger使用教程(图文介绍,一篇就够了)

前言 日常开发中&#xff0c;接口都是和开发文档相结合的。不论是和前端对接还是三方对接亦或者是接口留档&#xff0c;当我们开发完接口后&#xff0c;都需要去创建对应的接口文档。而修改接口后也要修改相对应的接口文档&#xff0c;但是这个真的很容易疏漏。而且相对于繁重的…

WEB攻防【6】——Python考点/CTF与CMS/SSTI模板注入/PYC反编译

#知识点 1、PYC文件反编译 2、python-web-SSTI 3、SSTI模板注入利用分析 SSTI 就是服务器端模板注入 &#xff08;Server-Side Template Injection&#xff09; 当前使用的一些框架&#xff0c;比如python的flask&#xff0c;php的tp&#xff0c;java的spring等一般都采用成…

存储管理(三):分区表

什么是分区表 假设存在表t&#xff1a; CREATETABLE t (ftimedatetime NOT NULL,c int(11) DEFAULT NULL,KEY (ftime) )ENGINEInnoDB DEFAULT CHARSETlatin1 PARTITION BY RANGE (YEAR(ftime)) (PARTITION p_2017 VALUES LESS THAN (2017) ENGINE InnoDB,PARTITION p_2018 VA…

golang 获取系统的主机 CPU 内存 磁盘等信息

golang 获取系统的主机 CPU 内存 磁盘等信息 要求 需要go1.18或更高版本 官方地址&#xff1a;https://github.com/shirou/gopsutil 使用 #下载包 go get github.com/shirou/gopsutil/v3/cpu go get github.com/shirou/gopsutil/v3/disk go get github.com/shirou/gopsuti…

tr、cut、split、grep -E

目录 tr命令&#xff1a;替换和删除 cut命令&#xff1a;快速裁剪 split命令&#xff1a;文件拆分 文件合并 面试题 1.现在有一个日志文件&#xff0c;有5个G&#xff0c;能不能快速的打开 2.cat合并和paste合并之间的区别&#xff1f; 3.统计当前主机的连接状态&#…

Hadoop3:MapReduce中的Reduce Join和Map Join

一、概念说明 学过MySQL的都知道&#xff0c;join和left join 这里的join含义和MySQL的join含义一样 就是对两张表的数据&#xff0c;进行关联查询 Hadoop的MapReduce阶段&#xff0c;分为2个阶段 一个Map&#xff0c;一个Reduce 那么&#xff0c;join逻辑&#xff0c;就可以…

前端开发的工厂设计模式

在前端开发中&#xff0c;工厂设计模式&#xff08;Factory Pattern&#xff09;是一种非常有用的设计模式&#xff0c;能够帮助我们在创建对象时减少代码的重复性和复杂性。 一、工厂设计模式概述 工厂设计模式是一种创建型设计模式&#xff0c;主要目的是定义一个用于创建对…

2024年建筑八大员(资料员)考试题库,省心高效,轻松通过!

1.插入的图片无法显示&#xff0c;或者显示失真&#xff0c;正确做法是&#xff08;&#xff09;。 A.插人图片是应选中【自动调整图片大小】 B.在下拉【菜单】中选中【按单元格式大小】插入 C.在【格式】下拉中【图片】处打钩 D.在【属性】下拉中选中【工具显示】 答案&a…

两张图片怎样拼在一起?将两张图片拼在一起的几种方法介绍

两张图片怎样拼在一起&#xff1f;拼接两张图片是一种常见的编辑技巧&#xff0c;能够将不同的视觉元素融合成一个整体&#xff0c;从而创造出更加生动和丰富的图像效果。无论是为了设计创意作品、制作社交媒体内容&#xff0c;还是简单地为个人相册增添趣味&#xff0c;掌握如…

Element-UI 并排显示多个 disabled按钮的时候, 不生效问题解决

目录 Element-UI 并排显示多个 disabled按钮的时候&#xff0c; 不生效问题解决 解决方法&#xff1a; 运行结果&#xff1a; Element-UI 并排显示多个 disabled按钮的时候&#xff0c; 不生效问题解决 解决方法&#xff1a; Element-UI 并排显示多个 disabled按钮的时候&a…

spring boot 3.0.1多模块项目使用nacos动态配置

根pom文件增加&#xff0c;spring-cloud-alibaba包管理&#xff0c;注意版本spring-boot 3.0.3&#xff0c;spring-cloud-alibaba 2022.0.0.0-RC1 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0…

我的创作纪念日学期总结

&#x1f525;个人主页&#xff1a; Forcible Bug Maker &#x1f525;专栏&#xff1a; 关于博主 目录 &#x1f308;前言&#x1f525;我的期末考试&#x1f525;我的学期总结&#x1f525;对未来的展望&#x1f308;结语 &#x1f308;前言 本篇博客主要内容&#xff1a;博…

归并排序和计数排序

目录 1.归并排序1.1递归1.1基本思想1.2算法描述1.3画图解释1.4代码实现 1.2非递归 2.计数排序2.1基本思想2.2算法描述3.画图解释 1.归并排序 1.1递归 1.1基本思想 归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法&#xff08;Divide and Conquer&#xf…

【C++】动态内存管理new和delete

文章目录 一、C的内存管理方式二、new和delete的用法1.操作内置类型2.操作自定义内置类型 三、new和delete的底层实现1.operator new和operator delete函数2.new和delete的实现原理 四、定位new表达式五、malloc/free和new/delete的区别 一、C的内存管理方式 之前在C语言的动态…

kafka(四)消息类型

一、同步消息 1、生产者 同步发送的意思就是&#xff0c;一条消息发送之后&#xff0c;会阻塞当前线程&#xff0c;直至返回 ack。 由于 send 方法返回的是一个 Future 对象&#xff0c;根据 Futrue 对象的特点&#xff0c;我们也可以实现同 步发送的效果&#xff0c;只需在调…

【数据结构】计数排序等排序

&#x1f4e2;博客主页&#xff1a;https://blog.csdn.net/2301_779549673 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01; &#x1f4e2;本文由 JohnKi 原创&#xff0c;首发于 CSDN&#x1f649; &#x1f4e2;未来很长&#…