网站建设方面的论文东莞市南城装饰工程东莞网站建设

news/2025/10/3 20:08:35/文章来源:
网站建设方面的论文,东莞市南城装饰工程东莞网站建设,帮人家做家务的网站,哈尔滨工程招投标信息网hello#xff0c;大家好#xff0c;本讲我们一起聊一下常见的几个并发工具类的使用和坑#xff01; 在日常工作中#xff0c;我们经常会遇到多线程并发问题#xff0c;比如ThreadLocal、锁、ConcurrentHashMap、CopyOnWriteArrayList等。那么如何正常的使用呢#xff1f;… hello大家好本讲我们一起聊一下常见的几个并发工具类的使用和坑 在日常工作中我们经常会遇到多线程并发问题比如ThreadLocal、锁、ConcurrentHashMap、CopyOnWriteArrayList等。那么如何正常的使用呢下面我们来一探究竟 一、ThreadLocal ThreadLocal 相信大家都很熟悉了它是为了解决多线程的资源竞争问题的比如两个线程同时访问同一个变量并修改它我们需要保证两个线程不互相影响。是一种用于实现线程本地存储的工具类允许你为每个线程创建和维护独立的变量副本。这样每个线程都可以独立地改变它自己的副本而不会影响其他线程的副本。这对于需要在多线程环境中保持状态的情况特别有用而又不希望使用同步机制来共享状态。 废话不多说直接上代码 比如我们有一个 SpringBoot 的 Web 项目使用 ThreadLocal 来保存用户上下文信息。 代码如下 RequestMapping RestController public class ThreadLocalController {private static final ThreadLocal currentUser ThreadLocal.withInitial(() - null);GetMapping(wrong)public Map wrong(RequestParam(userId) Integer userId) {//设置用户信息之前先查询一次ThreadLocal中的用户信息String before Thread.currentThread().getName() : currentUser.get();//设置用户信息到ThreadLocalcurrentUser.set(userId);// 设置用户信息之后再查询一次ThreadLocal中的用户信息String after Thread.currentThread().getName() : currentUser.get();//汇总输出两次查询结果Map result new HashMap();result.put(before, before);result.put(after, after);return result;} }定义一个ThreadLocal类型的变量currentUser用于存储用户信息在wrong方法中在设置请求的用户 id 之前和之后分别获取一次currentUser存储的用户信息并将结果返回。 配置文件 server.tomcat.threads.max1将 tomcat 线程池的最大线程数设置成 1原因后面再解释。 第一次请求http://localhost:8080/wrong?userId1 请求结果 { before: http-nio-8080-exec-1:null, after: http-nio-8080-exec-1:1 }符合预期因为在设置用户之前currentUser中是没有值的。 第二次请求http://localhost:8080/wrong?userId2 请求结果 { before: http-nio-8080-exec-1:1, after: http-nio-8080-exec-1:2 }我们看这个请求结果就出现问题了按理说 before 应该也是 null若是用户 1 的话那我们在业务中通过currentUser中存储的用户操作数据时数据上体现的操作人和实际操作人不一致。 为什么会产生这种问题呢 springboot 程序是运行在 tomcat 上的而 tomcat 中是有线程池来处理这些请求的为了提高效率避免频繁创建销毁线程前面设置了server.tomcat.threads.max1也就是将 tomcat 最大线程设置为 1所有的请求都是通过这个线程执行的。而ThreadLocal类型的变量currentUser是数据线程级别的在第一次请求后线程并没有被销毁而是归还到了线程池中也就是线程中的变量还是存在的。所以第二次请求时就可以获取到第一次请求设置的变量。所以我们在使用 ThreadLocal 是注意的点 使用类似 ThreadLocal 工具来存放一些数据时需要特别注意在代码运行完后显式地去清空设置的数据 另外除了获取数据混乱的问题外还可能导致内存泄漏问题如每次请求都往ThreadLocal变量中放到数据一直没有得到清空从而导致内存泄漏。 正确的使用方式 GetMapping(right) public Map right(RequestParam(userId) Integer userId) {String before Thread.currentThread().getName() : currentUser.get();currentUser.set(userId);try {String after Thread.currentThread().getName() : currentUser.get();Map result new HashMap();result.put(before, before);result.put(after, after);return result;} finally {//在finally代码块中删除ThreadLocal中的数据确保数据不串currentUser.remove();} }二、ConcurrentHashMap ConcurrentHashMap 是 Java 中的一个线程安全的哈希表实现用于在多线程环境下高效地存储和检索键值对。它是 java.util.concurrent 包的一部分设计用于替代传统的 Hashtable 和同步包装的 HashMap通过 Collections.synchronizedMap 生成的同步 Map。 主要特点 高效并发ConcurrentHashMap 允许多个线程并发地读写数据而不会发生线程间的冲突。它通过分段锁在 Java 8 之前或 CAS 操作在 Java 8 及之后来实现高效的并发访问。 无锁读取读取操作通常不需要加锁能够在不锁定整个数据结构的情况下进行并发读取。 部分锁定在 Java 8 之前ConcurrentHashMap 使用分段锁Segment来减少锁的粒度。每个 Segment 是一个小的哈希表只有在写操作时才需要锁定特定的 Segment。Java 8 之后ConcurrentHashMap 使用了更为精细化的锁机制结合 CAS 操作来进一步提高并发性能。 不允许 null 键或值与 HashMap 不同ConcurrentHashMap 不允许存储 null 键或 null 值。 特别要注意的是ConcurrentHashMap 只能保证提供的原子性读写操作是线程安全的。 下面我们来解释下这句话。 假如有这样一个场景map 可以存入 1000 个数据现在map 中已有 900 个数据现在用 10 个线程往 map 中插入数据每次插入之前先查询下 map 中还需要多少数据然后放入。此时很多同学觉得使用ConcurrentHashMap可以解决这个所谓的线程并发问题其实不然上代码 RequestMapping(chm) RestController public class CHMController {Logger log LoggerFactory.getLogger(CHMController.class);//线程个数private static int THREAD_COUNT 10;//总元素数量private static int ITEM_COUNT 1000;//帮助方法用来获得一个指定元素数量模拟数据的ConcurrentHashMapprivate ConcurrentHashMapString, Long getData(int count) {return LongStream.rangeClosed(1, count).boxed().collect(Collectors.toConcurrentMap(i - UUID.randomUUID().toString(), Function.identity(),(o1, o2) - o1, ConcurrentHashMap::new));}GetMapping(wrong)public String wrong() throws InterruptedException {ConcurrentHashMapString, Long concurrentHashMap getData(ITEM_COUNT - 100);//初始900个元素log.info(init size:{}, concurrentHashMap.size());ForkJoinPool forkJoinPool new ForkJoinPool(THREAD_COUNT);//使用线程池并发处理逻辑forkJoinPool.execute(() - IntStream.rangeClosed(1, 10).parallel().forEach(i - {//查询还需要补充多少个元素int gap ITEM_COUNT - concurrentHashMap.size();log.info(gap size:{}, gap);//补充元素concurrentHashMap.putAll(getData(gap));}));//等待所有任务完成forkJoinPool.shutdown();forkJoinPool.awaitTermination(1, TimeUnit.HOURS);//最后元素个数会是1000吗log.info(finish size:{}, concurrentHashMap.size());return OK;}}运行结果 2024-10-21T18:30:35.78908:00 INFO 8228 --- [demo] [nio-8080-exec-1] com.csdn.demo.controller.CHMController : finish size:1700 2024-10-21T18:35:43.79308:00 INFO 8228 --- [demo] [nio-8080-exec-1] com.csdn.demo.controller.CHMController : init size:900 2024-10-21T18:35:43.79408:00 INFO 8228 --- [demo] [Pool-2-worker-1] com.csdn.demo.controller.CHMController : gap size:100 2024-10-21T18:35:43.79408:00 INFO 8228 --- [demo] [Pool-2-worker-4] com.csdn.demo.controller.CHMController : gap size:100 2024-10-21T18:35:43.79408:00 INFO 8228 --- [demo] [Pool-2-worker-3] com.csdn.demo.controller.CHMController : gap size:100 2024-10-21T18:35:43.79508:00 INFO 8228 --- [demo] [Pool-2-worker-5] com.csdn.demo.controller.CHMController : gap size:15 2024-10-21T18:35:43.79508:00 INFO 8228 --- [demo] [Pool-2-worker-1] com.csdn.demo.controller.CHMController : gap size:0 2024-10-21T18:35:43.79508:00 INFO 8228 --- [demo] [Pool-2-worker-2] com.csdn.demo.controller.CHMController : gap size:100 2024-10-21T18:35:43.79508:00 INFO 8228 --- [demo] [Pool-2-worker-6] com.csdn.demo.controller.CHMController : gap size:100 2024-10-21T18:35:43.79508:00 INFO 8228 --- [demo] [Pool-2-worker-1] com.csdn.demo.controller.CHMController : gap size:-181 2024-10-21T18:35:43.79508:00 INFO 8228 --- [demo] [Pool-2-worker-7] com.csdn.demo.controller.CHMController : gap size:-300 2024-10-21T18:35:43.79508:00 INFO 8228 --- [demo] [Pool-2-worker-5] com.csdn.demo.controller.CHMController : gap size:-315 2024-10-21T18:35:43.79608:00 INFO 8228 --- [demo] [nio-8080-exec-1] com.csdn.demo.controller.CHMController : finish size:1415 通过运行结果可以看到最后concurrentHashMap变量存入的数据为 1415 个而不是预期的 1000 个。 初始大小为 900 个正确每个线程查出来缺少的数据有 100 的有 15 的还有负数的显然是不对的最后 map 中存入的总数是 1415 造成这种结果的原因是查询缺少多少个和添加数据操作不是原子的这就解释了上面说的ConcurrentHashMap 只能保证提供的原子性读写操作是线程安全的。 解决方法也比较简单那就是加锁synchronized GetMapping(right) public String right() throws InterruptedException {ConcurrentHashMapString, Long concurrentHashMap getData(ITEM_COUNT - 100);log.info(init size:{}, concurrentHashMap.size());ForkJoinPool forkJoinPool new ForkJoinPool(THREAD_COUNT);forkJoinPool.execute(() - IntStream.rangeClosed(1, 10).parallel().forEach(i - {//下面的这段复合逻辑需要锁一下这个ConcurrentHashMapsynchronized (concurrentHashMap) {int gap ITEM_COUNT - concurrentHashMap.size();log.info(gap size:{}, gap);concurrentHashMap.putAll(getData(gap));}}));forkJoinPool.shutdown();forkJoinPool.awaitTermination(1, TimeUnit.HOURS);log.info(finish size:{}, concurrentHashMap.size());return OK; }这样做虽然可以解决原子问题但是并不能发挥出ConcurrentHashMap自身的能力。 其实我们可以使用ConcurrentHashMap提供的原子性方法 computeIfAbsent判断 Key 是否存在 Value如果不存在则把 Lambda 表达式运行后的结果放入 Map 作为 Value如果存在则通过increment方法加 1 private MapString, Long gooduse() throws InterruptedException {ConcurrentHashMapString, LongAdder freqs new ConcurrentHashMap(ITEM_COUNT);ForkJoinPool forkJoinPool new ForkJoinPool(THREAD_COUNT);forkJoinPool.execute(() - IntStream.rangeClosed(1, LOOP_COUNT).parallel().forEach(i - {String key item ThreadLocalRandom.current().nextInt(ITEM_COUNT);//利用computeIfAbsent()方法来实例化LongAdder然后利用LongAdder来进行线程安全计数freqs.computeIfAbsent(key, k - new LongAdder()).increment();}));forkJoinPool.shutdown();forkJoinPool.awaitTermination(1, TimeUnit.HOURS);//因为我们的Value是LongAdder而不是Long所以需要做一次转换才能返回return freqs.entrySet().stream().collect(Collectors.toMap(e - e.getKey(),e - e.getValue().longValue())); }这种方式提升了性能比synchronized。原因在于computeIfAbsent底层使用 Java 自带的 Unsafe 实现的 CAS。它在虚拟机层面确保了写入数据的原子性比加锁的效率高得多。 static final K,V boolean casTabAt(NodeK,V[] tab, int i,NodeK,V c, NodeK,V v) {return U.compareAndSetObject(tab, ((long)i ASHIFT) ABASE, c, v);}三、CopyOnWriteArrayList 最后我们简单的说说CopyOnWriteArrayList。从名字也可以看出来它的原理就是写时复制。但是使用不当可能会造成严重的性能问题。因为很多同学只知道他是写时复制却忽略了一个场景那就是它适用于读多写少的场景为什么呢我们来看它的源码 /*** Appends the specified element to the end of this list.** param e element to be appended to this list* return {code true} (as specified by {link Collection#add})*/public boolean add(E e) {synchronized (lock) {Object[] elements getArray();int len elements.length;Object[] newElements Arrays.copyOf(elements, len 1);newElements[len] e;setArray(newElements);return true;}}在添加元素时先复制出一个数组元素添加到复制出来的数组中最后在重新设置回去而复制数据这一步时非常耗时的。所以当我们的场景是读多写少时可以使用CopyOnWriteArrayList来解决线程安全问题。 好啦以上就是本篇文章要介绍的内容了欢迎小伙伴们一起讨论

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

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

相关文章

Spring框架常见的注解 - 实践

Spring框架常见的注解 - 实践2025-10-03 20:03 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important…

C# MVVM模式和Qt中MVC模式的比较 - 指南

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

图书馆网站建设公司秒拍wordpress插件

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kuan 的首页,持续学…

网站建设关键字优化网页制作基础教程visual studio code

Python面向对象编程 1.面向对象概念介绍1) 面相过程 —— 怎么做?2)面向对象 谁来做 2.类和对象2.1类2.2对象2.3类和对象的关系2.4类的设计2.5面向对象设计案例 士兵类设计2.6身份运算符 3.私有属性和私有方法3.1. 应用场景及定义方式 4.继承、多态重写父…

使用 Copilot AI + Blazor 编一个五子棋游戏

1. 创建 Blazor web工程,选Auto2. 打开 GitHub Copilot 窗口,输入提示词 使用 Blazor 编一个五子棋游戏3.复制代码测试 为了方便调试, 我们先把运行模式由 InteractiveAuto 改为 InteractiveServer 打开 App.razor 编辑…

关于VMware虚拟机如何下载-2025.10.3

关于VMware虚拟机如何下载-2025.10.3现在要想下载VMware有以下办法: 一:直接到这个链接:https://www.vmware.com/products/desktop-hypervisor/workstation-and-fusion 点击 DOWNLOAD FUSION OR WORKSTATION后在这个…

RAG核心特性:ETL - 指南

RAG核心特性:ETL - 指南pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", &q…

国庆集训做题10.1 - 10.3

国庆集训做题 CSP-S模拟25 t1 : 爱丽丝的数位划分 题意简述 : 将序列A划分为k个不相交连续非空子序列,求最大的总优美度。 优美度指子序列中十进制表示数字不同的个数,一个方案的优美度是所有子序列优美度的和 首先…

免费申请网站空间网站转化率分析工具

Java线程6种状态和工作原理详解,Java创建线程的4种方式 目录 一、Java线程的六种状态 二、Java线程是如何工作的? 三、BLOCKED 和 WAITING 的区别 四、start() 和 run() 源码分析 五、Java创建线程的所有方式和代码详解 1. 继承Thread类 2. 实现…

政务公开网站建设情况从哪里下载wordpress

以前安装这个软件的时候, 是在windows和mac上,都是图形化的安装方式,但是ubuntu不太一样,需要增加源,然后执行命令。安装的系统版本是2004。 参考链接1,主要命令包含下面几个部分: 第一步&…

XCSY暑期集训模拟赛2T3善良

XCSY暑期集训模拟赛2T3善良暴力(50pts) 对于每个询问,遍历\([l,r]\),统计其中k的数量,时间复杂度为\(O(nm)\)。 for(int i=1;i<=n;i++) cin>>a[i]; while(m--){cin>>l>>r>>k;int ans=0;…

玳瑁的嵌入式日记---0928(ARM--UART) - 指南

玳瑁的嵌入式日记---0928(ARM--UART) - 指南pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", &q…

网站开发需要那些技能电子商务平台中搜索词拆解时

产生原因:因为项目最近设计到了一个Quartz相关的模块&#xff0c;前端需要传递时间参数到后台, 然后后台设置一个新的定时任务, 所以后台需要一个可以实现Date与cron之间的相互转换(因为Quartz需要的Cron格式的数据)&#xff0c;所以就借助java的SimpleDateFormat的格式化,然后…

解决Visual Studio中无法使用scanf和C++万能头的问题

解决Visual Studio中无法使用scanf和C++万能头的问题Visual Studio中无法使用scanf和C++万能头? 来吧,跟我一起操作! 1.在桌面上创建万能头文件, 并命名为stdc++.h: 记得把文件后缀名给打开哟! 2.用记事本打开std…

太仓专业网站建设我想创个网站

目录 项目背景 项目技术栈 项目介绍 项目亮点 项目启动 1.创建SSM&#xff08;省略&#xff09; 2.配置项目信息 3.将前端页面加入到项目中 4.初始化数据库 5.创建标准分层的目录 6.创建和编写项目中的公共代码以及常用配置 7.创建和编写业务的Entity、Mapper、…

网页站点不安全静海网站开发

虚拟机是软件 对于第一次听说虚拟机&#xff08;Virtual Machine&#xff0c;VM&#xff09;的人来说&#xff0c;可能以为还要再花钱买一台计算机&#xff0c;这恐怕是他们最担心的。所谓虚拟机&#xff0c;就是在你的计算机上再虚拟出另一台计算机来。这台虚拟出来的计算机&…

技术培训学校机构做360手机网站优化

1.1. Socket简介 套接字&#xff08;socket&#xff09;是一种通信机制&#xff0c;凭借这种机制&#xff0c; 客户端<->服务器 模型的通信方式既可以在本地设备上进行&#xff0c;也可以跨网络进行。 Socket英文原意是“孔”或者“插座”的意思&#xff0c;在网络编程…

北京正规制作网站公司网页无法访问qq可以登陆

默认情况下&#xff0c;所有的新邮箱数据都是启用Exchange搜索&#xff0c;仅当多个邮箱迁移到该Exchange Server时&#xff0c;才禁用搜索索引。 获取数据库的Exchange搜索索引 使用Get-MailboxDatabase 来获取数据库的搜索索引 Get-MailboxDatabase | Select-Object Name,…

做爰全过程免费网站的视频教程网站资讯创作

本文将介绍以下内容&#xff1a; • 面向对象基本概念 • 类和结构体简介 • 引用类型和值类型区别 1. 引言 提起class和struct&#xff0c;我们首先的感觉是语法几乎相同&#xff0c;待遇却翻天复地。历史将接力棒由面向过程编程传到面向对象编程&#xff0c;class和stru…

学校网站管理与建设办法怎样建立自己购物网站

主流开发语言 Java 简介&#xff1a;Java 是一种广泛使用的面向对象的编程语言&#xff0c;由Sun Microsystems公司于1995年发布&#xff0c;后由Oracle公司接手。Java具有“一次编写&#xff0c;到处运行”的特性&#xff0c;它的跨平台能力得益于Java虚拟机&#xff08;JVM&a…