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

news2024/12/26 11:53:38

🌸个人主页: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://www.coloradmin.cn/o/1649789.html

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

相关文章

力扣437. 路径总和 III

Problem: 437. 路径总和 III 文章目录 题目描述思路复杂度Code 题目描述 思路 1.定义int类型函数rootSum(root, targetSum)&#xff0c;用于求取每一个节点等于目标函数的路径数&#xff1a; 1.1.易知rootSum(root, targetSum)求出的数量等于rootSum(root.left, targetSum - va…

智慧公厕建设,打造智慧城市基础设施新亮点

公共厕所是城市基础设施的重要组成部分&#xff0c;而智慧公厕的建设则是现代城市管理的创新之举。为了实现公厕的精细化管理和提供更便捷的服务&#xff0c;推进智慧公厕建设必须要实现技术融合、业务融合、数据融合的目标&#xff0c;跨越层级、地域、系统、部门和业务的限制…

LeetCode:盛最多水的容器

文章收录于LeetCode专栏 盛最多水的容器 给你n个非负整数a1&#xff0c;a2&#xff0c;…&#xff0c;an&#xff0c;每个数代表坐标中的一个点(i, ai) 。在坐标内画 n 条垂直线&#xff0c;垂直线i的两个端点分别为(i, ai) 和 (i, 0)。找出其中的两条线&#xff0c;使得它们与…

自动化运维管理工具----------Ansible模块详细解读

目录 一、自动化运维工具有哪些&#xff1f; 1.1Chef 1.2puppet 1.3Saltstack 二、Ansible介绍 2.1Ansible简介 2.2Ansible特点 2.3Ansible工作原理及流程 2.3.1内部流程 2.3.2外部流程 三、Ansible部署 3.1环境准备 3.2管理端安装 ansible 3.3Ansible相关文件 …

机器学习第二天(监督学习,无监督学习,强化学习,混合学习)

1.是什么 基于数据寻找规律从而建立关系&#xff0c;进行升级&#xff0c;如果是以前的固定算式那就是符号学习了 2.基本框架 3.监督学习和无监督式学习&#xff1a; 监督学习&#xff1a;根据正确结果进行数据的训练&#xff1b; 在监督式学习中&#xff0c;训练数据包括输…

《Python编程从入门到实践》day21

# 昨日知识点回顾 设置背景颜色 在屏幕中央绘制飞船 # 今日知识点学习 12.5 重构&#xff1a;方法_check_events()和_update_screen() 12.5.1 方法_check_events() import sys import pygame from Settings import Settings from Ship import Shipclass AlienInvasion:"…

Day1| Java基础 | 1 面向对象特性

Day1 | Java基础 | 1 面向对象特性 基础补充版Java中的开闭原则面向对象继承实现继承this和super关键字修饰符Object类和转型子父类初始化顺序 多态一个简单应用在构造方法中调用多态方法多态与向下转型 问题回答版面向对象面向对象的三大特性是什么&#xff1f;多态特性你是怎…

基于Springboot+Vue的Java项目-鲜牛奶订购系统开发实战(附演示视频+源码+LW)

大家好&#xff01;我是程序员一帆&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;Java毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计 &am…

matplotlib和pandas与numpy

1.matplotlib介绍 一个2D绘图库&#xff1b; 2.Pandas介绍&#xff1a; Pandas一个分析结构化数据的工具&#xff1b; 3.NumPy 一个处理n纬数组的包&#xff1b; 4.实践&#xff1a;绘图matplotlip figure()生成一个图像实例 %matplotlib inline&#xff1a;图形直接在…

​​【收录 Hello 算法】3.3 数字编码

目录 3.3 数字编码 3.3.1 原码、反码和补码 3.3.2 浮点数编码 3.3 数字编码 Tip 在本书中&#xff0c;标题带有 * 符号的是选读章节。如果你时间有限或感到理解困难&#xff0c;可以先跳过&#xff0c;等学完必读章节后再单独攻克。 3.3.1 原码、反码和补码 在…

重装win11系统后找不到WiFi

由于电脑崩溃重装了系统&#xff0c;win11,装完之后WiFi图标不见了且网络适配器根本没有无线网络选项。 右键电脑》管理》网络适配器。 在刚装好系统时候并没有前两项&#xff0c;查了很多资料&#xff0c;比如 关机14s 重启&#xff0c;还有通过服务配置 WLAN AutoConfig 都…

值得收藏!修复Windows 10/11中找不到输出或输入设备的五种方法

序言 这篇文章主要关注处理声音输出/输入设备未发现的问题。它提供了许多可行的方法,帮助了许多Windows用户。阅读以下内容以找到你的解决方案。 最近,我将Windows 10更新到21H2,发现我的音频无法工作。当我把鼠标放在任务栏上的声音图标(上面有一个十字图标)上时,它会…

word:三线表的绘制【攻略】

word&#xff1a;三线表的绘制【攻略】 前言版权推荐word&#xff1a;三线表的绘制效果简单方法另外的方法 最后 前言 2024-5-7 18:25:08 以下内容源自《【攻略】》 仅供学习交流使用 版权 禁止其他平台发布时删除以下此话 本文首次发布于CSDN平台 作者是CSDN日星月云 博客…

docker jenkins 部署springboot项目

1、创建jenkins容器 1&#xff0c;首先&#xff0c;我们需要创建一个 Jenkins 数据卷&#xff0c;用于存储 Jenkins 的配置信息。可以通过以下命令创建一个数据卷&#xff1a; docker volume create jenkins_data启动 Jenkins 容器并挂载数据卷&#xff1a; docker run -dit…

视频素材库在哪里找免费手机版?8个可以用手机浏览的素材网

在视觉内容占据主导地位的今天&#xff0c;合适的视频素材可以大大提升项目的吸引力和效果。以下列出的视频素材网站为广告制作者、社交媒体策略师及电影制作人提供了从传统到现代风格的各种视频素材选择&#xff0c;满足不同的创作需求。 1. 蛙学府&#xff08;中国&#xff…

PTQ4SAM、Mamba-Attention、AniTalker、IceFormer、U-DiTs、CogDPM

本文首发于公众号&#xff1a;机器感知 PTQ4SAM、Mamba-Attention、AniTalker、IceFormer、U-DiTs、CogDPM PTQ4SAM: Post-Training Quantization for Segment Anything Segment Anything Model (SAM) has achieved impressive performance in many computer vision tasks. Ho…

【typescript 小秘籍 - 类型自动推导】

今天发现个typescript的小技巧&#xff0c;原来在vscode里面 typescript是可以根据数据&#xff0c;自动推导其类型的&#xff0c;这样就不用自己去手敲定义了。比如 鼠标移动到person上&#xff0c;可以看到 其自动推导了person的类型 然后直接复制下来 直接使用即可。

Redis学习(十)|使用消息队列的重试机制实现 MySQL 和 Redis 的数据一致性

文章目录 介绍原理整体方案实现步骤示例代码总结其他&#xff1a;Kafka 重试策略配置1. 生产者重试策略配置2. 消费者重试策略配置 介绍 在分布式系统中&#xff0c;保持 MySQL 和 Redis 之间的数据一致性是至关重要的。为了确保数据的一致性&#xff0c;我们通常采取先更新数…

【Three.js基础学习】15.scroll-based-animation

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 前言 课程要点 结合html等场景 做滚动动画 1.遇到的问题&#xff0c; 在向下滚动时&#xff0c;下方会显白&#xff08;部分浏览器&#xff09; 解决&#xff1a;alpha:true …

什么是多模态大模型,有了大模型,为什么还要多模态大模型?

随着人工智能技术的愈演愈烈&#xff0c;其技术可以说是日新月异&#xff0c;每隔一段时间就会有新的技术和理念被创造出来&#xff1b;而多模态大模型也是其中之一。 什么是多模态 想弄明白什么是多模态大模型&#xff0c;那么首先就要弄明白什么是多模态。 简单来说&#x…