[Java EE] 多线程(九):JUC剩余部分与线程安全的集合类(多线程完结)

news2025/1/23 0:56:51

🌸个人主页: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
感谢点赞与关注~~~
在这里插入图片描述

目录

    • 2.2 手动锁ReentrantLock
      • 2.2.1 用法
    • 2.2.2 ReentrantLock与synchronized的区别
    • 2.3 信号量Semaphore
    • 2.4 CountDownLatch
    • 2.5 线程池
    • 2.6 原子类
    • 3. 线程安全的集合类
    • 3.1 多线程情况下使用ArrayList
    • 3.2 多线程使用队列
    • 3.3 多线程环境下使用哈希表
      • 3.3.1 Hashtable
      • 3.3.2 ConcurrentHashMap(高频面试题)

2.2 手动锁ReentrantLock

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

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

2.2.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.3 信号量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();
    }
}

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

2.4 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("所有线程任务结束");
    }
}

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

2.5 线程池

前面叙述过,不再赘述
https://blog.csdn.net/2301_80050796/article/details/138388735?spm=1001.2014.3001.5501

2.6 原子类

前面叙述过,不再赘述
https://blog.csdn.net/2301_80050796/article/details/138506092?spm=1001.2014.3001.5501

3. 线程安全的集合类

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

3.1 多线程情况下使用ArrayList

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

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

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

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

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

3.2 多线程使用队列

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

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

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

  • Hashtable
  • ConcurrentHashMap

3.3.1 Hashtable

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

3.3.2 ConcurrentHashMap(高频面试题)

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

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

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

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

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

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

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

相关文章

视频短信时代来临!发送前必知的四大关键要素

随着移动通信技术的迅猛发展&#xff0c;视频短信作为全新的沟通方式&#xff0c;正逐渐融入我们的日常生活。作为行业的先行者&#xff0c;邦之信已率先推出视频短信业务&#xff0c;并获得了市场的广泛认可。 那么&#xff0c;在发送视频短信时&#xff0c;我们需要注意哪些关…

【循环程序设计-谭浩强适配】(适合专升本、考研)

无偿分享学习资料&#xff0c;需要的小伙伴评论区或私信dd。。。 无偿分享学习资料&#xff0c;需要的小伙伴评论区或私信dd。。。 无偿分享学习资料&#xff0c;需要的小伙伴评论区或私信dd。。。 完整资料如下&#xff1a;纯干货、纯干货、纯干货&#xff01;&#xff01;…

启动项目时出现SELinux is preventing

问题描述 启动项目时出现SELinux is preventing**** SELinux正在阻止systemd对文件AB.sevice进行读取访问。 我的是启zabbix是报该错&#xff1a; 最终解决方案 方法一&#xff1a;暂时禁用SELinux setenforce 0 方法二&#xff1a;禁用SELinux 在配置文件/etc/sysconfig/…

【LeetCode】每日一题 2024_5_14 完成所有任务需要的最少轮数(哈希)

文章目录 LeetCode&#xff1f;启动&#xff01;&#xff01;&#xff01;题目&#xff1a;完成所有任务需要的最少轮数题目描述代码与解题思路 每天进步一点点 LeetCode&#xff1f;启动&#xff01;&#xff01;&#xff01; 题目&#xff1a;完成所有任务需要的最少轮数 题…

酒店前台装智能酒精壁炉前和装后对比

在酒店前台装智能酒精壁炉之前和之后&#xff0c;前台区域的氛围和效果会有显著的对比&#xff1a; 装智能酒精壁炉之前 传统或简约风格&#xff1a; 在壁炉安装之前&#xff0c;前台可能具有传统或简约的设计风格&#xff0c;可能缺乏独特的装饰元素或焦点。前台区域可能显…

OrangePi Zero2 全志H616开发学习文档、基础IO蜂鸣器、超声波测距、舵机PWM基础开发

一.平台介绍 OrangePi开发板不仅仅是一款消费品&#xff0c;同时也是给任何想用技术来进行创作创新的人设计的。它是一款简单、有趣、实用的工具&#xff0c;你可以用它去打造你身边的世界。 特性 CPU 全志H616四核64位1.5GHz高性能Cortex-A53处理器GPU MaliG31MP2 Supports…

LeetCode—用栈实现队列

1.题目 2.思路—双栈 思路与双队列实现栈类似&#xff1a; 将一个栈当作输入栈&#xff0c;用于压入push传入的数据&#xff1b;另一个栈当作输出栈&#xff0c;用于pop和peek操作。 每次 pop或 peek时&#xff0c;若输出栈outStack为空则将输入栈inStack的全部数据依次出栈并…

前端笔记-day06

文章目录 01-浮动-基本使用02-浮动-产品布局03-浮动-清除浮动带来的影响04-清除浮动-额外标签法05-清除浮动-单伪元素法06-清除浮动-双伪元素法单伪元素和双伪元素CSS 07-清除浮动-overflow08-flex布局-体验09-flex布局-组成10-flex布局-主轴对齐方式11-flex布局-侧轴对齐方式1…

如何用Rust获取CPU、内存、硬盘的信息?

目录 一、用Rust获取CPU、内存、硬盘的信息 二、知识点 systemstat 一、用Rust获取CPU、内存、硬盘的信息 首先&#xff0c;需要添加systemstat库到Cargo.toml文件&#xff1a; [dependencies] systemstat "0.2.3" 在Rust代码中使用它&#xff1a; extern crat…

400元已到账,成交从认真开始

昨天发了一个值班的需求&#xff0c;收到了很多好友的响应&#xff0c;这里非常感谢关注创业程序员卡酷的老朋友、新朋友。今天分享一下&#xff1a;拓展、合作、成交 现在不管是IT行业还是其他行业&#xff0c;大环境可谓一片惨淡&#xff0c;35乃至30找不到工作的失业人员一抓…

el-table 多级表头 列添加fixed=left 列错乱

如图错乱显示 添加 this.$refs.tableList.doLayout();

Linux 操作系统多路IO复用

1.多路IO复用 多路I/O复用是通过一种机制&#xff0c;可以监视多个描述符&#xff0c;一旦某个描述符就绪&#xff08;一般是读就绪或者写就绪&#xff09;&#xff0c;能够通知程序进行相应的读写操作。 这个机制能够通过select/poll/eroll等来使用。这些函数都可以同时监视多…

答辩PPT框架如何搭建?文心一言AI辅助构建

很多快要毕业的同学在做答辩PPT的时候总是感觉毫无思路&#xff0c;一窍不通。但这并不是你们的错&#xff0c;对于平时没接触过相关方面&#xff0c;第一次搞答辩PPT的人来说&#xff0c;这是很正常的一件事。一个好的答辩PPT可以根据以下分为以下几部分来写。 1.研究的背景和…

数据可视化(九):Pandas北京租房数据分析——房源特征绘图、箱线图、动态可视化等高级操作

Tips&#xff1a;"分享是快乐的源泉&#x1f4a7;&#xff0c;在我的博客里&#xff0c;不仅有知识的海洋&#x1f30a;&#xff0c;还有满满的正能量加持&#x1f4aa;&#xff0c;快来和我一起分享这份快乐吧&#x1f60a;&#xff01; 喜欢我的博客的话&#xff0c;记得…

数据可视化(十一):Pandas餐饮信息表分析——交叉表、离群点分析,多维分析等高级操作

Tips&#xff1a;"分享是快乐的源泉&#x1f4a7;&#xff0c;在我的博客里&#xff0c;不仅有知识的海洋&#x1f30a;&#xff0c;还有满满的正能量加持&#x1f4aa;&#xff0c;快来和我一起分享这份快乐吧&#x1f60a;&#xff01; 喜欢我的博客的话&#xff0c;记得…

简述RocketMQ系统架构及其相关概念

一、概述 RocketMQ是一款高性能、高吞吐量的分布式消息队列系统&#xff0c;它采用了分布式架构&#xff0c;支持多生产者和消费者并发读写&#xff0c;具有高可用性、高吞吐量、低延迟等特点。本文将对RocketMQ的系统架构进行详细解析。 二、架构设计 RocketMQ采用了分布式架…

AI如何改变PPT制作流程?推荐四款ai生成ppt工具

提起PPT&#xff0c;大家的第一反应就是痛苦。经常接触PPT的学生党和打工人&#xff0c;光看到这3个字母&#xff0c;就已经开始头痛了&#xff1a; 1、PPT内容框架与文案挑战重重&#xff0c;任务艰巨&#xff0c;耗费大量精力。 2、PPT的排版技能要求高&#xff0c;并非易事…

ATA-4315高压功率放大器在高频理疗仪驱动中的应用

现代社会&#xff0c;面对生活压力、工作压力&#xff0c;再加上不良的生活习惯&#xff0c;难免身体会出现这样或那样的问题&#xff0c;随着科技的发展&#xff0c;我们除了来到医院进行具体的治疗&#xff0c;一些可以用来辅助医生进行治疗的仪器设备也越来越多的应用到了临…

JavaScript 对象入门:基础用法全解析

目录 对象 语法 属性和访问 方法和调用 this关键字 null 遍历对象 内置对象 Math 属性 方法 Date 创建日期对象 获取和设置日期 ⭐对象 对象是 JavaScript 数据类型的一种&#xff0c;数据类型也包括数值类型、字符串类型、布尔类型、undefined。对象数据类型可…

Apache访问控制与虚拟主机

目录 一. Web服务简介 以下是一些 Web 服务的基本概念和特征 以下是一些主流的 Web 服务器 WEB 服务协议 二. Apache 服务的搭建与配置 2.1 Apache 介绍 2.2 Apache安装 2.3 Apache目录介绍 三. 访问控制 四. 修改默认网站发布目录 五. 虚拟主机 5.1 基于域名的虚拟…