[Java EE] 多线程(九):ReentrantLock,Semaphore,CountDownLatch与线程安全的集合类(多线程完结)

🌸个人主页:https://blog.csdn.net/2301_80050796?spm=1000.2115.3001.5343
🏵️热门专栏:🍕 Collection与数据结构 (91平均质量分)https://blog.csdn.net/2301_80050796/category_12621348.html?spm=1001.2014.3001.5482
🧀Java EE(94平均质量分) https://blog.csdn.net/2301_80050796/category_12643370.html?spm=1001.2014.3001.5482
🍭MySql数据库(93平均质量分)https://blog.csdn.net/2301_80050796/category_12629890.html?spm=1001.2014.3001.5482
感谢点赞与关注~~~
在这里插入图片描述
感谢关注,欢迎点赞和收藏~~~

ReentrantLock,Semaphore,CountDownLatch与线程安全的集合类

  • 1. 手动锁ReentrantLock
    • 1.1 用法
    • 1.2 ReentrantLock与synchronized的区别
  • 2. 信号量Semaphore
  • 3. CountDownLatch
  • 4. 线程安全的集合类
    • 4.1 多线程情况下使用ArrayList
    • 4.2 多线程使用队列
    • 4.3 多线程环境下使用哈希表
      • 4.3.1 Hashtable
      • 4.3.2 ConcurrentHashMap(高频面试题)

1. 手动锁ReentrantLock

可重入互斥锁,和synchronized功能类似,都是用来实现互斥效果,保证线程安全.

1.1 用法

  • lock(),加锁,如果获取不到锁就会死等.
  • tryLock(),枷锁,如果一定时间内获取不到锁就放弃加锁.
  • unlock(),解锁.
    在进行加锁解锁操作的时候,为了防止加锁之后未解锁的情况,我们在使用ReentrantLock的时候,一般使用try-finally结构来完成.未解锁操作并不是程序员忘记写unlock造成的,而是中间出现了一些例如在执行到某个时候直接return,或者在某些时候抛出异常,使得程序终止的操作.这时候unlock操作就执行不到了.而finally可以完美地解决这个问题.

代码如下:

public class Demo29 {public static void main(String[] args) {ReentrantLock reentrantLock = new ReentrantLock();//公平reentrantLock.lock();try {//...}finally {reentrantLock.unlock();}}
}

1.2 ReentrantLock与synchronized的区别

  • ReentrantLock需要手动加锁和手动解锁,而synchronized自动加锁解锁.
  • synchronized在申请失败的时候,会死等,而ReentrantLock可以使用tryLock方法来限制等待的时间.
  • synchronized是非公平锁,而ReentrantLock可以通过构造方法来规定这把锁是否是公平锁.
public ReentrantLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();}
ReentrantLock reentrantLock = new ReentrantLock(true);//公平
ReentrantLock reentrantLock1 = new ReentrantLock(false);//非公平
  • ReentrantLock拥有强大的唤醒机制,他可以指定线程唤醒,而synchronized如果有多个线程在wait的时候,notify只能唤醒随机的其中一个线程.

2. 信号量Semaphore

信号量,就是一个表示资源剩余个数的量,本质上就是一个计数器.

举例说明:停车场自动化停车
在每一个停车场的起杆的地方,都会显示当前剩余车位:xxx,只要有一辆车进入停车场,显示器上的停车位就会-1(称为p操作),有一辆车出来,就会+1(称为v操作),当车位已满的时候,停车场的杆就不会自动抬起.想要进来的车就要阻塞等待,等待有车从停车场出来才可以进去.
在这里插入图片描述

Semaphore的操作都是原子的,可以在多线程环境下直接使用.
代码实例:

  • acquire用来申请资源,release用来释放资源.
class demo{public static void main(String[] args) throws InterruptedException {Semaphore semaphore = new Semaphore(4);for (int i = 0; i < 4; i++) {semaphore.acquire();System.out.println("申请第"+(i+1)+"个资源");}for (int i = 0; i < 4; i++) {semaphore.release();System.out.println("释放第"+(i+1)+"个资源");}}
}

运行结果:
在这里插入图片描述

  • 如果在资源使用完之后,再去申请资源,就会阻塞等待.
class demo{public static void main(String[] args) throws InterruptedException {Semaphore semaphore = new Semaphore(4);for (int i = 0; i < 4; i++) {semaphore.acquire();System.out.println("申请第"+(i+1)+"个资源");}semaphore.acquire();}
}

运行结果:
在这里插入图片描述
我们看到,进行并未结束.

3. CountDownLatch

这个类是一个比较实用的工具类,他的主要功能就是:当一个任务被拆分成许多部分让多个线程执行的时候,就可以通过这个类来判断任务是否全部执行完毕.
就比如我们经常用到的一个下载工具:IDM下载器,这个下载工具的下载速度非常快,就是因为这个下载工具会把下载任务拆分成多个任务,等待所有下载任务全部结束之后,载合并下载结果,这时候就需要有一个工具来判断所有任务是否全部下载完毕.

举例说明:跑步比赛
10个选手同时起泡,有的跑的慢,有的跑的快,但是裁判必须等到所有运动员全部通过终点之后才可以结束比赛.

代码示例:

  • 使用countDown方法领取任务.
  • 使用await方法判断所有线程是否执行完毕.
import java.util.concurrent.CountDownLatch;public class Demo30 {public static void main(String[] args) throws InterruptedException {CountDownLatch latch = new CountDownLatch(5);//任务被拆分成了5个部分for (int i = 0; i < 5; i++) {//把任务分配给5个线程int finalI = i;Thread thread = new Thread(()->{try {Thread.sleep((finalI +1)*1000);} catch (InterruptedException e) {throw new RuntimeException(e);}latch.countDown();//获取任务System.out.println("任务"+(finalI+1)+"结束");});thread.start();}latch.await();//等待所有线程结束任务System.out.println("所有线程任务结束");}
}

运行结果:
在这里插入图片描述

4. 线程安全的集合类

原来的集合类,大多数都是线程不安全的.但是其中有几个线程是安全的.比如:Vector,Stack,HashTable.这些集合的方法都自带synchronized,都是被synchronized修饰的.但是加了锁也不一定安全,需要具体问题具体分析.

4.1 多线程情况下使用ArrayList

  1. 自己使用同步机制,synchronized或者ReentrantLock.
  2. Collections.synchronizedList(new ArrayList)
    这个操作相当于给ArrayList套了一层壳.相当于之后对ArrayList的操作都带上了synchronized修饰.
    上面两种操作都是有加锁的操作,下面我们介绍一种不加锁的操作.
  3. 使用CopyOnWriteArrayList.
    CopyOnWrite即写时拷贝容器.

如果我们想要修改一个容器中的值的时候,如果直接进行修改,比如想要修改两个数据,一个线程刚好修改完第一个数据的时候,有第二个线程想要来读取修改后的数据,这时候就读到的是一种"中间结果",不够准确.

这时候就需要引入写时拷贝容器:

  • 当我们往一个容器中添加或者修改数据的时候,不直接修改当前容器,而是先拷贝当前容器,之后在复制出的容器中进行修改.
  • 在修改完成之后,将原容器的引用指向修改后的容器.
    这样如果在有线程去读取数据的时候,如果修改未完成的时候,读取的就是原容器的数据,修改完成之后,就是读取新容器的数据了.所以CopyOnWrite容器采用的便是读写分离思想.

举例说明:不停机更新
在我们玩一个游戏,比如王者荣耀的时候,经常会出现不停机更新这样的现象.在更新的时候,并不会影响用户的游戏体验,在一场游戏结束之后,自动获取游戏更新内容.
在这里插入图片描述

4.2 多线程使用队列

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

4.3 多线程环境下使用哈希表

HashMap本身是线程不安全的.在多线程环境下使用哈希表可以使用:

  • Hashtable
  • ConcurrentHashMap

4.3.1 Hashtable

只是简单地把关键方法加上了synchronized.这就相当于直接对Hashtable对象本身直接加锁.
如果一个哈希表只有一把锁,别说在操作同一个哈希桶,即使在不同的线程操作不同的哈希桶的时候,也会产生阻塞,这样的效率是非常低的.而且一旦触发扩容,就会有大量拷贝的操作,这样的效率是非常低的.
在这里插入图片描述

4.3.2 ConcurrentHashMap(高频面试题)

相比于Hashtable做出了一些优化.

  • 优化1:ConcurrentHashMap对每一个哈希桶都使用了synchronized进行加锁,这就大大降低了锁冲突的概率.或许有的人会想,每个哈希桶都加锁,不是加大了加锁开销吗,其实不是,当没有线程与当前哈希桶进行锁竞争的时候,加锁的锁只是一个偏向锁,开销也没有大多少.反而降低锁冲突的概率收益会很明显.
    在这里插入图片描述

  • 优化2:充分利用了CAS特性.比如size属性就通过CAS来更新.避免出现重量级锁的情况.

  • 优化3: 优化了扩容方式:化整为零

    • 当发现需要扩容的时候,就会创建一个新的数组出来,同时只拷贝几个元素过去.
    • 扩容期间,新数组和老数组同时存在.
    • 后续的每个对ConcurrentHashMap的操作都会拷贝几个元素过去.总的来说不是一次性全部拷贝完成,而是分多次拷贝.
    • 之后每次插入新元素的时候都直接插入新数组中.
    • 当拷贝完成最后一个元素的时候,老数组就会被删除.

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

一文读懂Python的`__init__`,`__init__`方法的终极指南

大家好&#xff0c;今天给大家介绍一个Python中一个特殊的函数__init__。 在Python中&#xff0c;__init__方法是一个特殊的函数&#xff0c;它在创建类的新实例时自动调用。它的作用类似于其他编程语言中的构造函数&#xff0c;用于初始化对象的状态。这篇文章将带你深入了解…

python实现的信号合成分析系统(DSP)

python实现的信号合成分析系统(DSP) 流程 1、在QT界面上设置好信号频率,采样频率,采样点数 2、使用np构建sin函数 3、使用matplotlib画出 4、分别分析合成信号的FFT频域信息1、效果图 2、示例代码 def btn_com_clicked(self):# 信号合成分析Fs = self.com_fs_edit_value #…

【网络编程】http协议

预备知识 什么是http协议 HTTP&#xff08;Hypertext Transfer Protocol&#xff0c;超文本传输协议&#xff09;是一个应用层的协议&#xff0c;用于在网络中传输超文本&#xff08;如HTML文档&#xff09;。HTTP协议建立在TCP/IP协议之上&#xff0c;是Web浏览器和Web服务器…

Map集合的实现类~HashMap

存储结构&#xff1a;哈希表 键重复依据是hashCode和equals方法&#xff08;键不能重复&#xff09; 添加&#xff1a; 先创建Student类&#xff0c;那么往HashSet添加的就是Student对象作为键值&#xff0c;后面的作为值 删除&#xff1a; 判断&#xff1a; 遍历&#xff1a…

为什么要梯度累积

文章目录 梯度累积什么是梯度累积如何理解理解梯度累积梯度累积的工作原理 梯度累积的数学原理梯度累积过程如何实现梯度累积 梯度累积的可视化 梯度累积 什么是梯度累积 随着深度学习模型变得越来越复杂&#xff0c;模型的训练通常需要更多的计算资源&#xff0c;特别是在训…

self-attention 的 CUDA 实现及优化 (上)

self-attention 的 CUDA 实现及优化 (上) 导 读 self-attention 是 Transformer 中最关键、最复杂的部分&#xff0c;也是 Transformer 优化的核心环节。理解 self-attention &#xff0c;对于深入理解 Transformer 具有关键作用&#xff0c;本篇主要就围绕 self-attention 展…

java+jsp+Oracle+Tomcat 记账管理系统论文(完整版)

⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️ ➡️点击免费下载全套资料:源码、数据库、部署教程、论文、答辩ppt一条龙服务 ➡️有部署问题可私信联系 ⬆️⬆️⬆️​​​​​​​⬆️…

ThingsBoard版本控制配合Gitee实现版本控制

1、概述 2、架构 3、导出设置 4、仓库 5、同步策略 6、扩展 7、案例 7.1、首先需要在Giitee上创建对应同步到仓库地址 ​7.2、giit仓库只能在租户层面进行配置 7.3、 配置完成后&#xff1a;检查访问权限。显示已成功验证仓库访问&#xff01;表示配置成功 7.4、添加设…

鸿蒙OpenHarmony南向:【Hi3516标准系统入门(命令行方式)】

Hi3516标准系统入门&#xff08;命令行方式&#xff09; 注意&#xff1a; 从3.2版本起&#xff0c;标准系统不再针对Hi3516DV300进行适配验证&#xff0c;建议您使用RK3568进行标准系统的设备开发。 如您仍然需要使用Hi3516DV300进行标准系统相关开发操作&#xff0c;则可能会…

【Linux】文件内容相关的命令,补充:管道符

1、查看文件内容 &#xff08;1-1&#xff09;查看文件内容&#xff1a;cat&#xff0c;tac&#xff0c;head&#xff0c;tail 查看文件内容cat 文件名查看文件内容并显示行号cat -n 文件名倒着查看文件内容&#xff08;从最后一行开始&#xff09;tac 文件名查看文件前10行…

力扣hot100:543. 二叉树的直径/108. 将有序数组转换为二叉搜索树

一、543. 二叉树的直径 LeetCode&#xff1a;543. 二叉树的直径 二叉树的直径 二叉树的 直径 是指树中任意两个节点之间最长路径的 长度 。 遇到二叉树的问题很容易去直接用求解的目标去定义递归函数。但是仔细考虑&#xff0c;返回树的直径并不能向上传播。因此我们可以拆…

SolidWorks进行热力学有限元分析二、模型装配

1.先打开软件&#xff0c;新建装配体 2.选中你要装配的零件&#xff0c;直接导入就行 3.鼠标点击左键直接先放进去 4.开始装配&#xff0c;点配合 5.选择你要接触的两个面&#xff0c;鼠标右键确定&#xff0c;然后把剩下的面对齐一下就行了 6.搞定

学习《现代密码学——基于安全多方计算协议的研究》 第一章

目录 前言 第1章 绪论 1.1 密码学的发展历史 1.2 现代密码学体制 1.3 现代密码学与安全多方计算 前言 近几年来&#xff0c;云计算、物联网、移动互联网等新概念、新技术被先后提出&#xff0c;促使信息技术飞速发展。同时&#xff0c;人类生活、沟通方式也随着新技术的普及…

泰克MDO3024示波器如何调整衰减倍数?

泰克MDO3024示波器是一款高性能的数字示波器&#xff0c;具备多种功能和调节选项&#xff0c;可以满足各种测试需求。其中一个重要的调节选项就是调整衰减倍数&#xff0c;通过调整衰减倍数&#xff0c;可以改变示波器的灵敏度和测量范围&#xff0c;帮助我们更好地观察和分析信…

奇诡 matlab 小 bug matlab git需要记录的改动太多

似乎是我有一次添加了太多的路径之后的事情。但是不敢说一定是这个导致的&#xff1a; 症状&#xff1a;只要对文本进行任何编辑操作&#xff0c;工作区就会出现"Processing … Cancel"的提示&#xff0c;如果不管的话这个提示不会消失&#xff0c;同时matlab变得越来…

【进程终止】退出信号 | 三种退出情况 | 如何进程终止returnexit_exit

目录 退出码 退出信号 进程终止情况3 如何进程终止 return退出 库函数exit 系统调用函数_exit ​exit和_exit的区别缓冲区 exit _exit 退出码 回顾上篇 代码跑完&#xff0c;结果正确&#xff08;退出码为0&#xff09;代码跑完&#xff0c;结果不正确&#xff08;退…

springboot项目组合定时器schedule注解实现定时任务

springboot项目组合定时器schedule注解实现定时任务&#xff01; 创建好springboot项目后&#xff0c;需要在启动类上增加注解开启定时器任务 下图所示&#xff1a; 增加这个注解&#xff0c;启动项目&#xff0c; package com.example.scheduledemo.util;import org.springf…

Linux进程通信-信号

信号概念 信号是 Linux 进程间通信的最古老的方式之一&#xff0c;是事件发生时对进程的通知机制&#xff0c;有时也称之为软件中断&#xff0c;它是在软件层次上对中断机制的一种模拟&#xff0c;是一种异步通信的方式。信号 可以导致一个正在运行的进程被另一个正在运行的异…

企业怎样进行IT外包以及IT外包服务内容

在数字化时代的浪潮中&#xff0c;企业逐渐认识到信息技术的关键作用&#xff0c;特别是制造业基地对于IT外包和运维服务的需求持续增长。然而&#xff0c;在诸多可供选择的IT外包和运维方案中&#xff0c;企业如何推动与IT外包公司的合作&#xff1f;本文将深入介绍IT外包方案…

nginx 启动,查看,停止

nginx 启动&#xff0c;查看&#xff0c;停止 启动 start nginx 查看是否启动成功 tasklist | findstr nginx 停止 nginx -s stop 测试配置文件的语法是否有误 nginx -t 重启nginx nginx-s reload