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

多线程-进阶(2)CountDownLatchConcurrentHashMapSemaphore

目的;

JUC(java.util.concurrent) 的常⻅类

接着上一节课到

1.信号量 Semaphore

信号量, ⽤来表⽰ "可⽤资源的个数". 本质上就是⼀个计数器。
理解信号量
可以把信号量想象成是停⻋场的展⽰牌: 当前有⻋位 100 个. 表⽰有 100 个可⽤资源.
当有⻋开进去的时候, 就相当于申请⼀个可⽤资源, 可⽤⻋位就 -1 (这个称为信号量的 P 操作)
当有⻋开出来的时候, 就相当于释放⼀个可⽤资源, 可⽤⻋位就 +1 (这个称为信号量的 V 操作)如果计数器的值已经为 0 了, 还尝试申请资源, 就会阻塞等待, 直到有其他线程释放资源.

 Semaphore 的 PV 操作中的加减计数器操作都是原⼦的, 可以在多线程环境下直接使⽤.

代码⽰例:
创建 Semaphore ⽰例, 初始化为 4, 表⽰有 4 个可⽤资源.
acquire ⽅法表⽰申请资源(P操作), release ⽅法表⽰释放资源(V操作)
创建 20 个线程, 每个线程都尝试申请资源, sleep 1秒之后, 释放资源. 观察程序的执⾏效果.
    public static void main(String[] args) {Semaphore semaphore = new Semaphore(3);Runnable runnable = new Runnable() {@Overridepublic void run() {try {System.out.println("申请资源");semaphore.acquire();System.out.println("我获取到资源了");Thread.sleep(1000);System.out.println("我释放资源了");semaphore.release();} catch (InterruptedException e) {e.printStackTrace();}}};for (int i = 0; i < 10; i++) {Thread t = new Thread(runnable);t.start();}}

输出结果:

2. CountDownLatch

同时等待 N 个任务执⾏结束.

案例:

构造 CountDownLatch 实例, 初始化 10 表⽰有 10 个任务需要完成.
每个任务执⾏完毕, 都调⽤ latch.countDown() . 在 CountDownLatch 内部的计数器同时⾃减.
主线程中使⽤ latch.await(); 阻塞等待所有任务执⾏完毕. 相当于计数器为 0 了.

代码:

public static void main(String[] args) throws InterruptedException {CountDownLatch latch = new CountDownLatch(10);Runnable r = new Runnable() {@Overridepublic void run() {try {Thread.sleep(10000);latch.countDown();} catch (Exception e) {e.printStackTrace();}}};for (int i = 0; i < 10; i++) {new Thread(r).start();}// 必须等到 10 ⼈全部回来latch.await();System.out.println("⽐赛结束");}

 解释:

  1. /**第一步:设置线程A的运行次数为2/

  2. CountDownLatch latch = new CountDownLatch(10);

  1. /**第二步:递减锁存器的计数,如果计数到达零,则释放所有等待的线程**/

  2. latch.countDown();

  1. /**第三步:使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断

  2. * 如果当前的计数为零,则此方法立即返回 **/

  3. latch.await();

 CountDownLatch主要两个方法就是一是CountDownLatch.await()阻塞当前线程,二是CountDownLatch.countDown()当前线程把计数器减一。

相关⾯试题 

1. 线程同步的⽅式有哪些?
synchronized, ReentrantLock, Semaphore 等都可以⽤于线程同步.
2. 为什么有了 synchronized 还需要 juc 下的 lock?
以 juc 的 ReentrantLock 为例,
  1. synchronized 使⽤时不需要⼿动释放锁. ReentrantLock 使⽤时需要⼿动释放. 使⽤起来更灵活,
  2. synchronized 在申请锁失败时, 会死等. ReentrantLock 可以通过 trylock 的⽅式等待⼀段时间就放弃.
  3. synchronized 是⾮公平锁, ReentrantLock 默认是⾮公平锁. 可以通过构造⽅法传⼊⼀个 true 开启 公平锁模式.
  4. synchronized 是通过 Object 的 wait / notify 实现等待-唤醒. 每次唤醒的是⼀个随机等待的线程.ReentrantLock 搭配 Condition 类实现等待-唤醒, 可以更精确控制唤醒某个指定的线程.

 3. AtomicInteger 的实现原理是什么?

基于 CAS 机制,原子性

4. 信号量听说过么?之前都⽤在过哪些场景下?

信号量, ⽤来表⽰ "可⽤资源的个数". 本质上就是⼀个计数器.
使⽤信号量可以实现 "共享锁", ⽐如某个资源允许 3 个线程同时使⽤, 那么就可以使⽤ P 操作作为加锁, V 操作作为解锁, 前三个线程的 P 操作都能顺利返回, 后续线程再进⾏ P 操作就会阻塞等待, 直到前⾯的线程执⾏了 V 操作.
5. 解释⼀下 ThreadPoolExecutor 构造⽅法的参数的含义

3.线程安全的集合类

原来的集合类, ⼤部分都不是线程安全的.
Vector, Stack, HashTable, 是线程安全的(不建议⽤), 其他的集合类不是线程安全的.

3.1多线程环境使⽤ ArrayList

3.1.1.Collections.synchronizedList(new ArrayList);

synchronizedList 是标准库提供的⼀个基于 synchronized 进⾏线程同步的 List. synchronizedList
的关键操作上都带有 synchronized,可以使ArrayLis的大多数方法加锁,t变成线程安全的。

3.1.2.使⽤ CopyOnWriteArrayList 

CopyOnWrite容器即写时复制的容器
当我们往⼀个容器添加元素的时候,不直接往当前容器添加,⽽是先将当前容器进⾏Copy,复制
出⼀个新的容器,然后新的容器⾥添加元素,添加完元素之后,再将原容器的引⽤指向新的容器
这样做的好处是我们可以对CopyOnWrite容器进⾏并发的读,⽽不需要加锁,因为当前容器不会添
加任何元素。 所以CopyOnWrite容器也是⼀种读写分离的思想,读和写不同的容器。
优点:
在读多写少的场景下, 性能很⾼, 不需要加锁竞争.
缺点:
1. 占⽤内存较多.
2. 新写的数据不能被第⼀时间读取到

3.2多线程环境使⽤队列 

1. ArrayBlockingQueue
基于数组实现的阻塞队列
2. LinkedBlockingQueue
基于链表实现的阻塞队列
3. PriorityBlockingQueue
基于堆实现的带优先级的阻塞队列
4. TransferQueue
最多只包含⼀个元素的阻塞队列

3.3多线程环境使⽤哈希表(重点)

HashMap 本⾝不是线程安全的.
在多线程环境下使⽤哈希表可以使⽤:
Hashtable
ConcurrentHashMap

1) Hashtable

只是简单的把关键⽅法加上了 synchronized 关键字。

这相当于直接针对 Hashtable 对象本⾝加锁.
  1. 如果多线程访问同⼀个 Hashtable 就会直接造成锁冲突.
  2. size 属性也是通过 synchronized 来控制同步, 也是⽐较慢的.
  3. ⼀旦触发扩容, 就由该线程完成整个扩容过程. 这个过程会涉及到⼤量的元素拷⻉, 效率会⾮常低.

 

 2) ConcurrentHashMap

相⽐于 Hashtable 做出了⼀系列的改进和优化. 以 Java1.8 为例

1)读操作没有加锁(但是使⽤了 volatile 保证从内存读取结果), 只对写操作进⾏加锁. 加锁的⽅式仍然是是⽤ synchronized, 但是不是锁整个对象, ⽽是 "锁桶" (⽤每个链表的头结点作为锁对象), ⼤⼤降低了锁冲突的概率.
2)充分利⽤ CAS 特性. ⽐如 size 属性通过 CAS 来更新. 避免出现重量级锁的情况.
3)  优化了扩容⽅式: 化整为零
发现需要扩容的线程, 只需要创建⼀个新的数组, 同时只搬⼏个元素过去.
扩容期间, 新⽼数组同时存在.
后续每个来操作 ConcurrentHashMap 的线程, 都会参与搬家的过程. 每个操作负责搬运⼀⼩部分元素.
搬完最后⼀个元素再把⽼数组删掉.
这个期间, 插⼊只往新数组加.
这个期间, 查找需要同时查新数组和⽼数组

好了今天就到这里了。

相关文章:

多线程-进阶(2)CountDownLatchConcurrentHashMapSemaphore

目的; JUC(java.util.concurrent) 的常⻅类 接着上一节课到 1.信号量 Semaphore 信号量, ⽤来表⽰ "可⽤资源的个数". 本质上就是⼀个计数器。 理解信号量 可以把信号量想象成是停⻋场的展⽰牌: 当前有⻋位 100 个. 表⽰有 100 个可⽤资源. 当有⻋开进去的时候,…...

密码管理器KeePass的安装及使用

文章目录 软件下载安装汉化新建数据库创建\移动\修改 群组添加/修改/删除/移动 记录展示、搜索、锁定单独使用keepass生成密码的功能AES-256的密钥长度为256位&#xff0c;为啥可以设置超过32个字符的密钥&#xff1f; 软件下载 安装 分别解压&#xff1a;KeePass-2.53.1.zip&…...

星海智算:【萤火遛AI-Stable-Diffusion】无需部署一键启动

部署流程 1、注册算力云平台&#xff1a;星海智算 https://gpu.spacehpc.com/ 2、创建实例&#xff0c;镜像请依次点击&#xff1a;“镜像市场”->“更换”->“AI绘画”->“萤火遛AI-Stable Diffusion”。 程序首次启动可能需要几分钟&#xff0c;待实例显示“运行…...

JS生成器的特殊用法:委托yield*

yield 的基本用法 yield 用于在生成器函数中暂停函数执行&#xff0c;并返回一个值给外部调用者。当生成器再次被调用时&#xff0c;会从暂停的地方继续执行。 示例&#xff1a; function* simpleGenerator() {yield 1;yield 2;yield 3; }const gen simpleGenerator();cons…...

【CuPy报错】NVRTC_ERROR_COMPILATION (6)找不到 ‘vector_types.h‘

cupy安装不要再使用pip install cupy了&#xff0c; 已经替换成基于版本安装了pip install cupy-cuda12x&#xff0c;详见cupy官网。 安装完成后&#xff0c;在import cupy之后报错&#xff0c;找不到 ‘vector_types.h’: CompileException: /home/zoe/venv/lib/python3.10/…...

机器学习:知识蒸馏(Knowledge Distillation,KD)

知识蒸馏&#xff08;Knowledge Distillation&#xff0c;KD&#xff09;作为深度学习领域中的一种模型压缩技术&#xff0c;主要用于将大规模、复杂的神经网络模型&#xff08;即教师模型&#xff09;压缩为较小的、轻量化的模型&#xff08;即学生模型&#xff09;。在实际应…...

【C++入门篇 - 3】:从C到C++第二篇

文章目录 从C到C第二篇new和delete命名空间命名空间的访问 cin和coutstring的基本使用 从C到C第二篇 new和delete 在C中用来向系统申请堆区的内存空间 New的作用相当于C语言中的malloc Delete的作用相当于C语言中的free 注意&#xff1a;在C语言中&#xff0c;如果内存不够…...

YOLOv8模型改进 第七讲 一种新颖的注意力机制 Outlook Attention

随着目标检测技术的不断发展&#xff0c;YOLOv8 作为最新一代的目标检测模型&#xff0c;已经在多个基准数据集上展现了其卓越的性能。然而&#xff0c;在复杂场景中&#xff0c;如何进一步提升模型的检测精度和鲁棒性依然是一个重要挑战。本文将探讨将 Outlook Attention 机制…...

C#多线程基本使用和探讨

线程是并发编程的基础概念之一。在现代应用程序中&#xff0c;我们通常需要执行多个任务并行处理&#xff0c;以提高性能。C# 提供了多种并发编程工具&#xff0c;如Thread、Task、异步编程和Parallel等。 Thread 类 Thread 类是最基本的线程实现方法。使用Thread类&#xff0…...

PHP DateTime基础用法

PHP DateTime 的用法详解 一、引言 在开发 PHP 应用程序时&#xff0c;处理日期和时间是一个至关重要的任务。PHP 提供了强大的日期和时间处理功能&#xff0c;其中 DateTime 类是最常用的工具之一。DateTime 类提供了丰富的方法来创建、格式化、计算和比较日期时间&#xff…...

一次Fegin CPU占用过高导致的事故

记录一下 一次应用事故分析、排查、处理 背景介绍 9号上午收到CPU告警&#xff0c;同时业务反馈依赖该服务的上游服务接口响应耗时太长 应用告警-CPU使用率 告警变更 【WARNING】项目XXX,集群qd-aliyun,分区bbbb-prod,应用customer,实例customer-6fb6448688-m47jz, POD实例CP…...

【Go初阶】两万字快速入门Go语言

初见golang语法 package mainimport "fmt"func main() {/* 简单的程序 万能的hello world */fmt.Println("Hello Go")} 第一行代码package main定义了包名。你必须在源文件中非注释的第一行指明这个文件属于哪个包&#xff0c;如&#xff1a;package main…...

【React】使用 react hooks 需要遵守的原则

1&#xff09;只能在顶层调用Hooks 这是指你不能在循环、条件语句或嵌套函数中调用Hooks。确保每次组件渲染时&#xff0c;Hooks的调用顺序保持一致。因此&#xff0c;你应该始终在React函数组件的最顶层调用Hooks。 React依赖于Hooks的调用顺序。如果这些调用在不同的渲染中顺…...

Python编程:创意爱心表白代码集

在寻找一种特别的方式来表达你的爱意吗&#xff1f;使用Python编程&#xff0c;你可以创造出独一无二的爱心图案&#xff0c;为你的表白增添一份特别的浪漫。这里为你精选了六种不同风格的爱心表白代码&#xff0c;让你的创意和情感通过代码展现出来。 话不多说&#xff0c;咱…...

腾讯IM SDK:TUIKit发送多张图片

一、问题描述 在使用腾讯IM DEMO&#xff08;https://github.com/TencentCloud/chat-uikit-vue.git&#xff09;时发现其只支持发送一张图片&#xff1a; 二、解决方案 // src\TUIKit\components\TUIChat\message-input-toolbar\image-upload\index.vue<inputref"inp…...

《本地部署开源大模型》在Ubuntu 22.04系统下ChatGLM3-6B高效微调实战

在Ubuntu 22.04系统下ChatGLM3-6B高效微调实战 无论是在单机单卡&#xff08;一台机器上只有一块GPU&#xff09;还是单机多卡&#xff08;一台机器上有多块GPU&#xff09;的硬件配置上启动ChatGLM3-6B模型&#xff0c;其前置环境配置和项目文件是相同的。如果大家对配置过程还…...

Python 脚本来自动发送每日电子邮件报告

安装必要的库 我们将使用 smtplib 发送邮件&#xff0c;以及 email.mime 来创建电子邮件内容。另外&#xff0c;为了让脚本自动定时运行&#xff0c;可以使用操作系统的计划任务工具&#xff08;如 Linux 的 cron 或 Windows 的 Task Scheduler&#xff09;。 创建邮件内容 使…...

大语言模型与ChatGPT:深入探索与应用

文章目录 1. 前言2. 大语言模型的概述2.1 什么是大语言模型&#xff1f;2.2 Transformer架构的核心2.3 预训练与微调 3. ChatGPT的架构与技术背景3.1 GPT模型的演进3.2 ChatGPT的工作原理 4. ChatGPT的实际应用4.1 日常对话助手4.2 内容生成与写作4.3 编程辅助4.4 教育与学习辅…...

【从零开始的LeetCode-算法】3164.优质数对的总数 II

给你两个整数数组 nums1 和 nums2&#xff0c;长度分别为 n 和 m。同时给你一个正整数 k。 如果 nums1[i] 可以被 nums2[j] * k 整除&#xff0c;则称数对 (i, j) 为 优质数对&#xff08;0 < i < n - 1, 0 < j < m - 1&#xff09;。 返回 优质数对 的总数。 示…...

FastDFS VS MinIO:文件存储与对象存储的抉择(包含SpringBoot集成FastDFS范例)

FastDFS vs MinIO&#xff1a;文件存储与对象存储的抉择&#xff08;包含SpringBoot集成FastDFS范例&#xff09; 我坐在窗边&#xff0c;随着飞机穿过云层&#xff0c;在云层之上滑翔。可以清晰的看到飞机在天空留下的痕迹&#xff0c;不知道那是蔚蓝中的纯白&#xff0c;还是…...

[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?

&#x1f9e0; 智能合约中的数据是如何在区块链中保持一致的&#xff1f; 为什么所有区块链节点都能得出相同结果&#xff1f;合约调用这么复杂&#xff0c;状态真能保持一致吗&#xff1f;本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里&#xf…...

【JavaEE】-- HTTP

1. HTTP是什么&#xff1f; HTTP&#xff08;全称为"超文本传输协议"&#xff09;是一种应用非常广泛的应用层协议&#xff0c;HTTP是基于TCP协议的一种应用层协议。 应用层协议&#xff1a;是计算机网络协议栈中最高层的协议&#xff0c;它定义了运行在不同主机上…...

如何在看板中体现优先级变化

在看板中有效体现优先级变化的关键措施包括&#xff1a;采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中&#xff0c;设置任务排序规则尤其重要&#xff0c;因为它让看板视觉上直观地体…...

掌握 HTTP 请求:理解 cURL GET 语法

cURL 是一个强大的命令行工具&#xff0c;用于发送 HTTP 请求和与 Web 服务器交互。在 Web 开发和测试中&#xff0c;cURL 经常用于发送 GET 请求来获取服务器资源。本文将详细介绍 cURL GET 请求的语法和使用方法。 一、cURL 基本概念 cURL 是 "Client URL" 的缩写…...

Kubernetes 网络模型深度解析:Pod IP 与 Service 的负载均衡机制,Service到底是什么?

Pod IP 的本质与特性 Pod IP 的定位 纯端点地址&#xff1a;Pod IP 是分配给 Pod 网络命名空间的真实 IP 地址&#xff08;如 10.244.1.2&#xff09;无特殊名称&#xff1a;在 Kubernetes 中&#xff0c;它通常被称为 “Pod IP” 或 “容器 IP”生命周期&#xff1a;与 Pod …...

6️⃣Go 语言中的哈希、加密与序列化:通往区块链世界的钥匙

Go 语言中的哈希、加密与序列化:通往区块链世界的钥匙 一、前言:离区块链还有多远? 区块链听起来可能遥不可及,似乎是只有密码学专家和资深工程师才能涉足的领域。但事实上,构建一个区块链的核心并不复杂,尤其当你已经掌握了一门系统编程语言,比如 Go。 要真正理解区…...

AD学习(3)

1 PCB封装元素组成及简单的PCB封装创建 封装的组成部分&#xff1a; &#xff08;1&#xff09;PCB焊盘&#xff1a;表层的铜 &#xff0c;top层的铜 &#xff08;2&#xff09;管脚序号&#xff1a;用来关联原理图中的管脚的序号&#xff0c;原理图的序号需要和PCB封装一一…...

Java并发编程实战 Day 11:并发设计模式

【Java并发编程实战 Day 11】并发设计模式 开篇 这是"Java并发编程实战"系列的第11天&#xff0c;今天我们聚焦于并发设计模式。并发设计模式是解决多线程环境下常见问题的经典解决方案&#xff0c;它们不仅提供了优雅的设计思路&#xff0c;还能显著提升系统的性能…...

渗透实战PortSwigger Labs指南:自定义标签XSS和SVG XSS利用

阻止除自定义标签之外的所有标签 先输入一些标签测试&#xff0c;说是全部标签都被禁了 除了自定义的 自定义<my-tag onmouseoveralert(xss)> <my-tag idx onfocusalert(document.cookie) tabindex1> onfocus 当元素获得焦点时&#xff08;如通过点击或键盘导航&…...

大数据驱动企业决策智能化的路径与实践

&#x1f4dd;个人主页&#x1f339;&#xff1a;慌ZHANG-CSDN博客 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; 一、引言&#xff1a;数据驱动的企业竞争力重构 在这个瞬息万变的商业时代&#xff0c;“快者胜”的竞争逻辑愈发明显。企业如何在复杂环…...