当前位置: 首页 > news >正文

【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());}
}

总结:

  1. Callable接口也用来描述一个任务,其中的call方法可以设置返回值将任务执行结果返回
  2. Runnable接口可以用来描述一个任务,但run方法的返回类型为void,无法返回任务结果
  3. callable需要经过FutureTask封装一下才能传入Thread构造方法,否则编译报错

至此创建线程有5种方式:

  1. 继承Thread
  2. 使用Runnable
  3. 使用lambda表达式
  4. 使用线程池/ThreadFactory
  5. 使用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有哪些其他价值?

  1. ReentrantLock 提供了公平锁,向ReentrantLock的构造方法里传入true,就是公平锁,ReentrantLock locker = new ReentrantLock(true);
  2. ReentrantLock提供了tryLock()操作,当尝试加锁,如果所以经被获取了,则返回操作失败,tryLock()还提供了带时间参数版本,可以等待一定时间,时间到了再返回操作失败
  3. 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&#xff0c;放了和多线程相关的组件 1.1 Callable 接口 看以下从计算从1加到1000的代码&#xff1a; public class Demo {public static int sum;public static void main(String[] args) throws InterruptedException {Thread …...

如何高效的向AI大模型提问? - 提示工程Prompt Engineering

大模型的输入&#xff0c;决定了大模型的输出&#xff0c;所以一个符合要求的提问Prompt起到关键作用。 以下是关于提示工程Prompt Engineering主要方法的详细表格&#xff0c;包括每种方法的优点、缺点、应用场景以及具体示例&#xff1a; 主要方法优点缺点应用场景示例明确性…...

4K高清壁纸网站推荐

1. Awesome Wallpapers 官网: https://4kwallpapers.com/ 主题: 创意、摄影、人物、动漫、绘画、视觉 分辨率: 4K Awesome Wallpapers 提供了丰富的高质量图片&#xff0c;分为通用、动漫、人物三大类&#xff0c;可以按屏幕比例和分辨率检索&#xff0c;满足你对壁纸的各种…...

EasyExcel注解使用

上接《Springboot下导入导出excel》&#xff0c;本篇详细介绍 EasyExcel 注解使用。 1. ExcelProperty value&#xff1a;指定写入的列头&#xff0c;如果不指定则使用成员变量的名字作为列头&#xff1b;如果要设置复杂的头&#xff0c;可以为value指定多个值order&#xff…...

Visual Basic 6 关于应用的类库 - 开源研究系列文章

上次整理VB6的原来写的代码&#xff0c;然后遇到了关于应用窗体的显示问题。VB6不比C#&#xff0c;能够直接反射调用里面的方法&#xff0c;判断是否显示关于窗体然后显示。以前写过一个VB6插件的例子&#xff0c;不过那个源码不在&#xff0c;所以就找了度娘&#xff0c;能够象…...

C#泛型

泛型是一种非常强大的特性&#xff0c;它允许程序员编写灵活的代码&#xff0c;同时保持类型安全。泛型的核心思想是允许类或方法在定义时不指定具体的数据类型&#xff0c;而是在实际使用时指定。这意味着你可以创建一个可以与任何数据类型一起工作的类或方法 泛型类通过在类…...

go语言的成神之路-标准库篇-fmt标准库

目录 一、三种类型的输出 print&#xff1a; println&#xff1a; printf&#xff1a; 总结&#xff1a; 代码展示&#xff1a; 二、格式化占位符 %s&#xff1a;用于格式化字符串。 %d&#xff1a;用于格式化整数。 %f&#xff1a;用于格式化浮点数。 %v&#xff1…...

React Native的router解析

写在前面 React Native&#xff08;简称RN&#xff09;是一个由Facebook开发的开源框架&#xff0c;用于构建跨平台的移动应用程序。在RN中&#xff0c;路由&#xff08;router&#xff09;是非常重要的概念&#xff0c;它允许我们在不同的屏幕之间进行导航和切换。 以下是RN…...

Linux update-alternatives 命令详解

1、查看所有候选项 sudo update-alternatives --list &#xff08;java筛选​​​​​​​sudo update-alternatives --list java&#xff09; 2、​​​​​​​更换候选项 sudo update-alternatives --config java 3、自动选择优先级最高的作为默认项 sudo update-alterna…...

【踩坑】修复报错libcurl.so.4、LIBFFI_BASE_7.0、libssl.so.3

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你&#xff0c;欢迎[点赞、收藏、关注]哦~ libcurl.so.4&#xff1a; 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段落&#xff0c;同时下载好本节将用到的库。下载方式为&#xff1a;pip install beautifulsoup4 一点碎碎念&#xff1a;为什么install后面的不是bs4也不是BeautifulSoup&#xff1f; html_doc """ <html><head><title>The…...

【已解决】MacOS上VMware Fusion虚拟机打不开的解决方法

在使用VMware Fusion时&#xff0c;不少用户可能会遇到虚拟机无法打开的问题。本文将为大家提供一个简单有效的解决方法&#xff0c;只需删除一个文件&#xff0c;即可轻松解决这一问题。 一、问题现象 在MacOS系统上&#xff0c;使用VMware Fusion运行虚拟机时&#xff0c;有…...

经典视觉神经网络1 CNN

一、概述 输入的图像都很大&#xff0c;使用全连接网络的话&#xff0c;计算的代价较高&#xff0c;图像也很难保留原本特征。 卷积神经网络&#xff08;Convolutional Neural Network&#xff0c;CNN&#xff09;是一种专门用于处理具有网格状结构数据的深度学习模型。主要应用…...

一些硬件知识【2024/12/6】

MP6924A: 正点原子加热台拆解&#xff1a; PMOS 相比 NMOS 的缺点&#xff1a; 缺点描述迁移率低PMOS 中的空穴迁移率约为电子迁移率的 1/3 到 1/2&#xff0c;导致导通电流较低。开关速度慢由于迁移率较低&#xff0c;PMOS 的开关速度比 NMOS 慢&#xff0c;不适合高速数字电…...

网络安全法-网络安全支持与促进

第二章 网络安全支持与促进 第十五条 国家建立和完善网络安全标准体系。国务院标准化行政主管部门和国务院其他有关部门根据各自的职责&#xff0c;组织制定并适时修订有关网络安全管理以及网络产品、服务和运行安全的国家标准、行业标准。 国家支持企业、研究机构、高等学…...

【Docker】如何在Docker中配置防火墙规则?

Docker本身并不直接管理防火墙规则&#xff1b;它依赖于主机系统的防火墙设置。不过&#xff0c;Docker在启动容器时会自动配置一些iptables规则来管理容器网络流量。如果你需要更细粒度地控制进出容器的流量&#xff0c;你需要在主机系统上配置防火墙规则。以下是如何在Linux主…...

Cesium 问题: 添加billboard后移动或缩放地球,标记点位置会左右偏移

文章目录 问题分析原先的:添加属性——解决漂移移动问题产生新的问题:所选的经纬度坐标和应放置的位置有偏差解决坐标位置偏差的问题完整代码问题 添加 billboard 后, 分析 原先的: // 图标加载 function addStation ({lon, lat, el, testName...

使用Python3 连接操作 OceanBase数据库

注&#xff1a;使用Python3 连接 OceanBase数据库&#xff0c;可通过安装 PyMySQL驱动包来实现。 本次测试是在一台安装部署OBD的OceanBase 测试linux服务器上&#xff0c;通过python来远程操作OceanBase数据库。 一、Linux服务器通过Python3连接OceanBase数据库 1.1 安装pyth…...

SpringBoot该怎么使用Neo4j - 优化篇

文章目录 前言实体工具使用 前言 上一篇中&#xff0c;我们的Cypher都用的是字符串&#xff0c;字符串拼接简单&#xff0c;但存在写错的风险&#xff0c;对于一些比较懒的开发者&#xff0c;甚至觉得之间写字符串还更自在快速&#xff0c;也确实&#xff0c;但如果在后期需要…...

Flutter如何调用java接口如何导入java包

文章目录 1. Flutter 能直接调用 Java 的接口吗&#xff1f;如何调用 Java 接口&#xff1f; 2. Flutter 能导入 Java 的包吗&#xff1f;步骤&#xff1a; 总结 在 Flutter 中&#xff0c;虽然 Dart 是主要的开发语言&#xff0c;但你可以通过**平台通道&#xff08;Platform …...

[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解

突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 ​安全措施依赖问题​ GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...

Linux链表操作全解析

Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表&#xff1f;1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...

8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂

蛋白质结合剂&#xff08;如抗体、抑制肽&#xff09;在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上&#xff0c;高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术&#xff0c;但这类方法普遍面临资源消耗巨大、研发周期冗长…...

Java入门学习详细版(一)

大家好&#xff0c;Java 学习是一个系统学习的过程&#xff0c;核心原则就是“理论 实践 坚持”&#xff0c;并且需循序渐进&#xff0c;不可过于着急&#xff0c;本篇文章推出的这份详细入门学习资料将带大家从零基础开始&#xff0c;逐步掌握 Java 的核心概念和编程技能。 …...

c#开发AI模型对话

AI模型 前面已经介绍了一般AI模型本地部署&#xff0c;直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型&#xff0c;但是目前国内可能使用不多&#xff0c;至少实践例子很少看见。开发训练模型就不介绍了&am…...

聊一聊接口测试的意义有哪些?

目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开&#xff0c;首…...

为什么要创建 Vue 实例

核心原因:Vue 需要一个「控制中心」来驱动整个应用 你可以把 Vue 实例想象成你应用的**「大脑」或「引擎」。它负责协调模板、数据、逻辑和行为,将它们变成一个活的、可交互的应用**。没有这个实例,你的代码只是一堆静态的 HTML、JavaScript 变量和函数,无法「活」起来。 …...

抽象类和接口(全)

一、抽象类 1.概念&#xff1a;如果⼀个类中没有包含⾜够的信息来描绘⼀个具体的对象&#xff0c;这样的类就是抽象类。 像是没有实际⼯作的⽅法,我们可以把它设计成⼀个抽象⽅法&#xff0c;包含抽象⽅法的类我们称为抽象类。 2.语法 在Java中&#xff0c;⼀个类如果被 abs…...

API网关Kong的鉴权与限流:高并发场景下的核心实践

&#x1f525;「炎码工坊」技术弹药已装填&#xff01; 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 引言 在微服务架构中&#xff0c;API网关承担着流量调度、安全防护和协议转换的核心职责。作为云原生时代的代表性网关&#xff0c;Kong凭借其插件化架构…...

车载诊断架构 --- ZEVonUDS(J1979-3)简介第一篇

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 做到欲望极简,了解自己的真实欲望,不受外在潮流的影响,不盲从,不跟风。把自己的精力全部用在自己。一是去掉多余,凡事找规律,基础是诚信;二是…...