【JavaEE】多线程(7)

一、JUC的常见类
JUC→java.util.concurrent,放了和多线程相关的组件
1.1 Callable 接口
看以下从计算从1加到1000的代码:
public class Demo {public static int sum;public static void main(String[] args) throws InterruptedException {Thread t = new Thread(new Runnable() {@Overridepublic void run() {int result = 0;for (int i = 1; i <= 1000; i++) {result += i;}sum = result;}});t.start();t.join();System.out.println(sum);}
}
通过成员变量的方式才能得到线程中任务的最终执行结果,这样会增加代码的耦合性,当改变外部的sum,线程内的sum也要改,所以引入Callable接口
public class Demo {public static void main(String[] args) throws InterruptedException, ExecutionException {Callable<Integer> callable = new Callable<Integer>() {@Overridepublic Integer call() throws Exception {int result = 0;for (int i = 1; i <= 1000; i++) {result += i;}return result;}};FutureTask<Integer> futureTask = new FutureTask<>(callable);Thread t = new Thread(futureTask);t.start();t.join();System.out.println(futureTask.get());}
}
总结:
- Callable接口也用来描述一个任务,其中的call方法可以设置返回值将任务执行结果返回
- Runnable接口可以用来描述一个任务,但run方法的返回类型为void,无法返回任务结果
- callable需要经过FutureTask封装一下才能传入Thread构造方法,否则编译报错
至此创建线程有5种方式:
- 继承Thread
- 使用Runnable
- 使用lambda表达式
- 使用线程池/ThreadFactory
- 使用Callable接口
2.2 ReentrantLock
ReentrantLock 是可重入锁,它将加锁和解锁两个操作进行了区分,提供了加锁:lock()方法 和解锁:unlock()方法
public class Demo {public static void main(String[] args) {ReentrantLock locker = new ReentrantLock();locker.lock();locker.unlock();}
}
其实这样并不好,因为在加锁和解锁之间可能会存在很多逻辑,假如在解锁之前就通过return返回了,或者抛出了异常,这样走不到解锁,所以一般将unlock写到finally中,finally中的代码无论如何都会被执行
public class Demo {public static void main(String[] args) {ReentrantLock locker = new ReentrantLock();try {locker.lock();} finally {locker.unlock();}}
}
synchronized也是可重入锁,那ReentrantLock相比于synchronized有哪些其他价值?
- ReentrantLock 提供了公平锁,向ReentrantLock的构造方法里传入true,就是公平锁,ReentrantLock locker = new ReentrantLock(true);
- ReentrantLock提供了tryLock()操作,当尝试加锁,如果所以经被获取了,则返回操作失败,tryLock()还提供了带时间参数版本,可以等待一定时间,时间到了再返回操作失败
- synchronized搭配wait notify等待通知机制,ReentrantLock搭配Condition类完成等待通知,Condition可以指定线程唤醒
1.3 信号量 Semaphore
信号量就是一个计数器,描述了可用资源的个数,有两个核心操作:
- P操作:计数器 -1,申请资源→acquire()
- V操作:计数器 +1,释放资源→release()
public class Demo {public static void main(String[] args) throws InterruptedException {Semaphore semaphore = new Semaphore(3);semaphore.acquire();semaphore.acquire();semaphore.acquire(); //最多申请3次//semaphore.acquire(); // 这里会进行阻塞, 直到有线程执行release为止semaphore.release(); //释放一个后, 就不会阻塞了semaphore.acquire();}
}
将信号量设为1,其作用就等价于一个锁,当一个线程申请资源后,另外一个线程再申请,就会进入阻塞,直到第一个线程释放资源
public class Demo {public static int count = 0;public static void main(String[] args) throws InterruptedException {Semaphore semaphore = new Semaphore(1);Thread t1 = new Thread(() -> {try {for (int i = 0; i < 50000; i++) {semaphore.acquire();count++;semaphore.release();}} catch (InterruptedException e) {throw new RuntimeException(e);}});Thread t2 = new Thread(() -> {try {for (int i = 0; i < 50000; i++) {semaphore.acquire();count++;semaphore.release();}} catch (InterruptedException e) {throw new RuntimeException(e);}});t1.start();t2.start();t1.join();t2.join();System.out.println("count = "+ count);}
}
1.4 CountDownLatch
当我们把一个任务拆分成多个的时候,当所有的任务(线程)都执行完后将结果合并,此时就可以使用这个工具类来识别所有线程是否都执行完了
public class Demo18 {public static void main(String[] args) throws InterruptedException {CountDownLatch latch = new CountDownLatch(10); // 配合下面的countDown 和 awaitfor (int i = 0; i < 10; i++) {int id = i;Thread t = new Thread(() -> {System.out.println("线程开始 "+ id);try {Thread.sleep(3000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("线程结束 "+ id);latch.countDown(); //每个线程执行完都执行一次countDown});t.start();}latch.await(); // 所有线程都执行完countDown 后再往下执行,否则这里阻塞等待System.out.println("所有线程执行结束");}
}
二、集合类的线程安全问题
Vector、Stack、HashTable这三个集合类是线程安全的,因为它们的方法都被synchronized修饰
2.1 多线程使用ArrayList
1)自己使用线程同步机制,加上synchronized或ReentrantLock
2)使用Collections.synchronizedList (new ArrayList);synchronizedList是一个基于synchronized的List,synchronizedList的关键操作上都带有synchronized
3)使用CopyOnWriteArrayList——写时拷贝;当我们向一个容器中插入元素时,会将原容器拷贝出一份新容器,往新容器里插入元素,插入完毕后,将原容器的引用指向新容器
写时拷贝的好处:解决线程安全问题,当在修改时,许多线程也在读,通过写时拷贝的处理,让线程只能在原容器读,在新容器写,写完后,原容器的引用指向新容器
2.2 多线程使用哈希表
HashMap 本身是线程不安全的,在多线程环境下可以使用:Hashtable 或 ConcurrentHashMap
1)Hashtable
在一些关键操作上加上synchronized,这相当于直接针对Hashtable对象本身加锁
- 多线程访问同一个Hashtable就会造成锁冲突,一个线程使用Hashtable中的关键方法时都会使其他要使用Hashtable的线程进入阻塞
- Hashtable 一旦触发扩容,就让触发扩容的线程完成整个扩容,效率非常低,因为涉及到大量元素的拷贝
2)ConcurrentHashMap
ConcurrentHashMap做了一些优化
1)在加锁方式上,使用"锁桶"的方式来代替一把"全局锁",ConcurrentHashMap在哈希表中的每一个链表都加上了锁

这样当两个不同的线程操作不同的链表的时候不会产生锁冲突,这样加了锁而且链表的个数通常很多,大部分情况都不会产生锁冲突,synchronized不产生锁冲突就是个偏向锁
2)size是真个哈希表的属性,不同的线程在执行插入或删除元素时会涉及到多个线程修改同一个变量,所以采用CAS的方式修改size,避免了加锁操作
【注意】这里不要和Hashtable搞混,Hashtable是一把全局锁,一个线程像哈希表中插入元素时,其他线程都在阻塞等待,直到插入完,所以不会涉及到多个线程修改size的情况;但ConcurrentHashMap 是锁桶的方式加锁,针对每个链表加锁,所以会有多个线程同一个时间段插入的情况,那么多个线程修改size的情况就不可避免,因此这里采用CAS的方式修改size
3)ConcurrentHashMap 针对扩容操作做了特殊优化,Hashtable是在触发扩容时,直接在那个线程的put方法里完成整个扩容,而ConcurrentHashMap在扩容会搞一个新数组,这个数组的容量是扩容后的容量
接下来要把旧数组中的数据拷贝到新数组中,但并不是一次性拷贝完,而是在之后,每次有线程进行哈希表的基本操作时,都会把一部分数据从旧数组搬运到新数组
在搬运的过程中:
- 插入:在新的数组插入
- 删除:新旧数组都要删除
- 查找:新就数组都要查找
🙉到此,多线程的篇章全部结束
相关文章:
【JavaEE】多线程(7)
一、JUC的常见类 JUC→java.util.concurrent,放了和多线程相关的组件 1.1 Callable 接口 看以下从计算从1加到1000的代码: public class Demo {public static int sum;public static void main(String[] args) throws InterruptedException {Thread …...
如何高效的向AI大模型提问? - 提示工程Prompt Engineering
大模型的输入,决定了大模型的输出,所以一个符合要求的提问Prompt起到关键作用。 以下是关于提示工程Prompt Engineering主要方法的详细表格,包括每种方法的优点、缺点、应用场景以及具体示例: 主要方法优点缺点应用场景示例明确性…...
4K高清壁纸网站推荐
1. Awesome Wallpapers 官网: https://4kwallpapers.com/ 主题: 创意、摄影、人物、动漫、绘画、视觉 分辨率: 4K Awesome Wallpapers 提供了丰富的高质量图片,分为通用、动漫、人物三大类,可以按屏幕比例和分辨率检索,满足你对壁纸的各种…...
EasyExcel注解使用
上接《Springboot下导入导出excel》,本篇详细介绍 EasyExcel 注解使用。 1. ExcelProperty value:指定写入的列头,如果不指定则使用成员变量的名字作为列头;如果要设置复杂的头,可以为value指定多个值orderÿ…...
Visual Basic 6 关于应用的类库 - 开源研究系列文章
上次整理VB6的原来写的代码,然后遇到了关于应用窗体的显示问题。VB6不比C#,能够直接反射调用里面的方法,判断是否显示关于窗体然后显示。以前写过一个VB6插件的例子,不过那个源码不在,所以就找了度娘,能够象…...
C#泛型
泛型是一种非常强大的特性,它允许程序员编写灵活的代码,同时保持类型安全。泛型的核心思想是允许类或方法在定义时不指定具体的数据类型,而是在实际使用时指定。这意味着你可以创建一个可以与任何数据类型一起工作的类或方法 泛型类通过在类…...
go语言的成神之路-标准库篇-fmt标准库
目录 一、三种类型的输出 print: println: printf: 总结: 代码展示: 二、格式化占位符 %s:用于格式化字符串。 %d:用于格式化整数。 %f:用于格式化浮点数。 %v࿱…...
React Native的router解析
写在前面 React Native(简称RN)是一个由Facebook开发的开源框架,用于构建跨平台的移动应用程序。在RN中,路由(router)是非常重要的概念,它允许我们在不同的屏幕之间进行导航和切换。 以下是RN…...
Linux update-alternatives 命令详解
1、查看所有候选项 sudo update-alternatives --list (java筛选sudo update-alternatives --list java) 2、更换候选项 sudo update-alternatives --config java 3、自动选择优先级最高的作为默认项 sudo update-alterna…...
【踩坑】修复报错libcurl.so.4、LIBFFI_BASE_7.0、libssl.so.3
转载请注明出处:小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你,欢迎[点赞、收藏、关注]哦~ libcurl.so.4: sudo apt install curl -y LIBFFI_BASE_7.0: conda install libffi3.3 -y libssl.so.3: sudo apt install -y openssl li…...
python网络爬虫基础:html基础概念与遍历文档树
开始之前导入html段落,同时下载好本节将用到的库。下载方式为:pip install beautifulsoup4 一点碎碎念:为什么install后面的不是bs4也不是BeautifulSoup? html_doc """ <html><head><title>The…...
【已解决】MacOS上VMware Fusion虚拟机打不开的解决方法
在使用VMware Fusion时,不少用户可能会遇到虚拟机无法打开的问题。本文将为大家提供一个简单有效的解决方法,只需删除一个文件,即可轻松解决这一问题。 一、问题现象 在MacOS系统上,使用VMware Fusion运行虚拟机时,有…...
经典视觉神经网络1 CNN
一、概述 输入的图像都很大,使用全连接网络的话,计算的代价较高,图像也很难保留原本特征。 卷积神经网络(Convolutional Neural Network,CNN)是一种专门用于处理具有网格状结构数据的深度学习模型。主要应用…...
一些硬件知识【2024/12/6】
MP6924A: 正点原子加热台拆解: PMOS 相比 NMOS 的缺点: 缺点描述迁移率低PMOS 中的空穴迁移率约为电子迁移率的 1/3 到 1/2,导致导通电流较低。开关速度慢由于迁移率较低,PMOS 的开关速度比 NMOS 慢,不适合高速数字电…...
网络安全法-网络安全支持与促进
第二章 网络安全支持与促进 第十五条 国家建立和完善网络安全标准体系。国务院标准化行政主管部门和国务院其他有关部门根据各自的职责,组织制定并适时修订有关网络安全管理以及网络产品、服务和运行安全的国家标准、行业标准。 国家支持企业、研究机构、高等学…...
【Docker】如何在Docker中配置防火墙规则?
Docker本身并不直接管理防火墙规则;它依赖于主机系统的防火墙设置。不过,Docker在启动容器时会自动配置一些iptables规则来管理容器网络流量。如果你需要更细粒度地控制进出容器的流量,你需要在主机系统上配置防火墙规则。以下是如何在Linux主…...
Cesium 问题: 添加billboard后移动或缩放地球,标记点位置会左右偏移
文章目录 问题分析原先的:添加属性——解决漂移移动问题产生新的问题:所选的经纬度坐标和应放置的位置有偏差解决坐标位置偏差的问题完整代码问题 添加 billboard 后, 分析 原先的: // 图标加载 function addStation ({lon, lat, el, testName...
使用Python3 连接操作 OceanBase数据库
注:使用Python3 连接 OceanBase数据库,可通过安装 PyMySQL驱动包来实现。 本次测试是在一台安装部署OBD的OceanBase 测试linux服务器上,通过python来远程操作OceanBase数据库。 一、Linux服务器通过Python3连接OceanBase数据库 1.1 安装pyth…...
SpringBoot该怎么使用Neo4j - 优化篇
文章目录 前言实体工具使用 前言 上一篇中,我们的Cypher都用的是字符串,字符串拼接简单,但存在写错的风险,对于一些比较懒的开发者,甚至觉得之间写字符串还更自在快速,也确实,但如果在后期需要…...
Flutter如何调用java接口如何导入java包
文章目录 1. Flutter 能直接调用 Java 的接口吗?如何调用 Java 接口? 2. Flutter 能导入 Java 的包吗?步骤: 总结 在 Flutter 中,虽然 Dart 是主要的开发语言,但你可以通过**平台通道(Platform …...
边缘计算医疗风险自查APP开发方案
核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...
云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地
借阿里云中企出海大会的东风,以**「云启出海,智联未来|打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办,现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...
MFC内存泄露
1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...
Day131 | 灵神 | 回溯算法 | 子集型 子集
Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣(LeetCode) 思路: 笔者写过很多次这道题了,不想写题解了,大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...
【位运算】消失的两个数字(hard)
消失的两个数字(hard) 题⽬描述:解法(位运算):Java 算法代码:更简便代码 题⽬链接:⾯试题 17.19. 消失的两个数字 题⽬描述: 给定⼀个数组,包含从 1 到 N 所有…...
ETLCloud可能遇到的问题有哪些?常见坑位解析
数据集成平台ETLCloud,主要用于支持数据的抽取(Extract)、转换(Transform)和加载(Load)过程。提供了一个简洁直观的界面,以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...
【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)
🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...
数据库分批入库
今天在工作中,遇到一个问题,就是分批查询的时候,由于批次过大导致出现了一些问题,一下是问题描述和解决方案: 示例: // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...
ios苹果系统,js 滑动屏幕、锚定无效
现象:window.addEventListener监听touch无效,划不动屏幕,但是代码逻辑都有执行到。 scrollIntoView也无效。 原因:这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作,从而会影响…...
学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”
2025年#高考 将在近日拉开帷幕,#AI 监考一度冲上热搜。当AI深度融入高考,#时间同步 不再是辅助功能,而是决定AI监考系统成败的“生命线”。 AI亮相2025高考,40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕,江西、…...
