[线程]JUC中常见的类 及 集合类在多线程下的线程安全问题

news2024/11/15 12:44:28

文章目录

  • 一. JUC中常见的类
    • 1. Callable接口
    • 2. ReentrantLock
    • 3. Semaphore 信号量
    • 4. CountDownLatch
  • 二. 集合类在多线程下的线程安全问题
    • 多线程下使用ArrayList
    • 多线程下使用哈希表(重要)

下面介绍的内容是面试中常考, 但是实际开发中用不到的知识

一. JUC中常见的类

JUC : java.util.cincurrent, 存放了很多和多线程相关的组件

1. Callable接口

与Runnable类似, Runnable描述了一个任务, 但是描述的任务run方法没有返回值
Callable也是描述一个任务, 但是call方法有返回值, 表示这个线程执行结束要得到啥结果

在这里插入图片描述
上述代码, 我们把result告知主线程
但是线程内部定义的局部变量是不能被其他线程获取到的
所以我们需要定义一个成员变量过度
在这里插入图片描述
但是这种方式, 就相当于让主线程和t线程耦合过大
那么Callable就可以优雅的解决上述问题
在这里插入图片描述
注意: Callable是不能直接填写在Thread构造方法中, 需要搭配:

在这里插入图片描述
完整代码:

public class Demo33 {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        Callable<Integer> callable = new Callable<Integer>() {
            @Override
            public 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 thread = new Thread(futureTask);
        thread.start();
        thread.join();
        //使用get获取到返回值
        System.out.println("result = " + futureTask.get());
    }
}

**(重点)总结一下:
线程创建的方式:

  1. 继承Thread
  2. 使用Runnable
  3. 使用lambda
  4. 使用线程池 / ThreadFactory
  5. 使用Callable

2. ReentrantLock

是JVM提供的一种锁, 可重入锁
当年早期的JVM中, synchronized没现在这么好用, 当时ReentrantLock还是非常有市场的
随着版本的对待, synchronized越来越强, 基本上遇到加锁的时候, 无脑用synchronized大概率不会出现问题

ReentrantLock是通过lock unlock的方式进行加锁解锁的
在这里插入图片描述
这种方式就可能会出现忘记加锁的情况, 所以我们一般搭配try-finally使用
在这里插入图片描述
与synchronized不同点有以下三个方面:

  1. ReentrantLock提供了公平锁的实现
    synchronized只是非公平锁
    在这里插入图片描述
    传true就是公平锁的形态, 写false或者不写, 就是非公平锁
  2. ReentrantLock提供tryLock操作, 给加锁提供了更多可能的空间
    tryLock尝试加锁, 如果锁已经被获取到了, 直接返回失败, 而不会继续阻塞等待
    tryLock操作还有一个版本, 可以指定等待的时间
    而synchronized是遇到锁竞争, 就阻塞等待
  3. ReentrantLock是搭配Condition类完成等待通知, Condition可以指定线程唤醒
    synchronized是搭配wait notify完成等待通知机制, notify只能唤醒等待线程中的一个

3. Semaphore 信号量

信号量就是一个计数器, 描述了可用资源的个数
围绕信号量有两个操作:

  1. P操作, 计数器-1, 申请资源
  2. V操作, 计数器+1, 释放资源

在Semaphore类中, P操作使用acquire, V操作使用release
在这里插入图片描述
如果申请资源数超过了初始值, 就会等待阻塞, 等其他进程进行V操作
在这里插入图片描述
锁, 其实就是特殊的信号量, 如果信号量只有0 1 两个取值, 此时就称为"二院信号量", 本质上就是一把锁
锁, 本质是个可用资源

4. CountDownLatch

当我们把一个任务拆分成很多个的时候, 可以通过这个工具类来识别任务是否整体执行完毕了
如果把一个任务拆分成10个任务, 那么每一个小任务完成后, 我们通过countDown方法来记录
使用await方法可以等待所有小任务结束
也就是await会阻塞等待吗直到countDown调用的次数, 和构造方法指定的此时一致的时候, await会返回
在这里插入图片描述

二. 集合类在多线程下的线程安全问题

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

但是上述集合类在使用过程中并不一定是线程安全的, 只是每个方法是带锁的
在这里插入图片描述
如果需要使用其他类, 就需要手动加锁
但是手动加锁比较麻烦, 标准库中提供了一下其他的解决方案

多线程下使用ArrayList

以ArrayList为例:

  1. 在这里插入图片描述
    使用这个方法, 就相当于给ArrayList这些集合类, 套一层壳, 给关键方法都加上了synchronized
  2. 使⽤ CopyOnWriteArrayList
    CopyOnWrite容器即写时拷贝的容器。
    当我们往⼀个容器添加元素的时候,不直接往当前容器添加,⽽是先将当前容器进⾏Copy,复制
    出⼀个新的容器,然后新的容器⾥添加元素
    添加完元素之后,再将原容器的引⽤指向新的容器
    但是当我们读的时候, 还是读旧的内容, 就不会读到错误的数据了

所以CopyOnWrite容器也是⼀种读写分离的思想,读和写不同的容器。

优点:
在读多写少的场景下, 性能很⾼, 不需要加锁竞争.
缺点:
1.占⽤内存较多.
2.新写的数据不能被第⼀时间读取到

多线程下使用哈希表(重要)

HashMap是线程不安全的
Hashtable是带锁的, 就是在每个方法头上加上了synchronized, 就相当于是针对this加锁
所以只要是针对Hashtable上的元素进行操作, 就会涉及到锁冲突, 效率是非常低的
想象一下哈希表的结构, 是数组加链表的形式, 如果我们针对不同的链表进行操作时, 是不会线程不安全问题的
ConcurrentHashMap做出了优化

  1. 使用"桶锁"的方式, 来代替一把"全局锁", 有效降低锁冲突的概率
    在这里插入图片描述
    如果两个线程, 针对不同的链表进行操作, 是不会涉及到锁冲突的
    上述的提升效率是非常大的, 因为大部分操作如果没有锁冲突了, 那么synchronized就是个偏向锁
  2. 引入CAS来修改公共变量
    像一些公共变量, 如size, 即使即使插入的是不同链表上的元素, 也会涉及到多线程修改同一个变量
    那么引入CAS, 通过CAS的方式, 来修改size, 也就避免了加锁操作
  3. 针对扩容操作做出了特殊的优化 ---- 化整为零
    如果发现, 负载因子太大了, 就需要扩容, 扩容是一个比较低效的操作
    普通的HashMap, 要在一次put操作的过程中完成扩容, 就会使put操作非常卡
    ConcurrentHashMap会在扩容的时候, 搞两份空间
    一份是扩容之前的空间
    一份是扩容之后的空间
    接下来, 没戏进行hash表的基本操作, 都会把一部分数据从旧空间搬运到新空间, 将数据分成多次搬
    插入操作: 插入到新空间
    删除操作: 新的旧的都要删除
    查找操作: 新的旧的都要查找

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

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

相关文章

『功能项目』主角身旁召唤/隐藏坐骑【20】

本章项目成果展示 我们打开上一篇19坐骑UI搭建及脚本控制显/隐的项目&#xff0c; 本章要做的事情是在坐骑UI界面点击召唤及隐藏坐骑的功能 首先在外包中拖拽一个坐骑熊的预制体 完全解压缩 重命名为MountBear 在资源文件夹Resources下的/预制体文件夹Prefabs下新建坐骑文件夹…

海外合规|新加坡网络安全认证计划简介(三)-Cyber Trust

一、 认证简介&#xff1a; Cyber Trust标志是针对数字化业务运营更为广泛的组织的网络安全认证。该标志针对的是规模较大或数字化程度较高的组织&#xff0c;因为这些组织可能具有更高的风险水平&#xff0c;需要他们投资专业知识和资源来管理和保护其 IT 基础设施和系统。Cy…

新质生产力人工智能+系列5-智能业务识别研究(含任务、数据、算力资源)

在新质生产力高质量发展的要求下&#xff0c;中国移动在“人工智能”和 “数据要素X”方面不断发力&#xff0c;持续发布高质量电信数据集。围绕网元智能、运维智能、服务智能三大方向建设&#xff0c;涵盖无线信道、基站、云网、核心网、哑资源等多领域&#xff0c;支持感知、…

护工系统|护工陪护系统|护工小程序

在医疗服务行业日新月异的今天&#xff0c;陪护机构正乘着数字化转型的东风&#xff0c;扬帆远航&#xff0c;其中&#xff0c;护工系统的引入无疑为其插上了一对强劲的翅膀&#xff0c;引领着行业向更加高效、精细化的方向迈进。这一系统不仅是对传统陪护模式的深刻重塑&#…

oracle物理存储结构文件详解

文章目录 oracle物理文件结构图① 控制文件&#xff1a;② 数据文件&#xff1a;③ 联机Redo日志文件&#xff1a;④ 参数文件&#xff1a;⑤ 归档文件&#xff1a;⑥ 密码文件&#xff1a; oracle物理文件结构图 Oracle数据库的物理结构由控制文件&#xff08;Control f…

硬件工程师笔试面试知识器件篇——电容

目录 电容 2.1、基础 电容原理图 电容实物图 2.1.1、定义 2.1.2、原理 2.1.3、电容的类型 分类1: 分类2: 2.1.4、电容的应用 2.2、相关问题 2.2.1、电容器的电容值如何测量 2.2.2、不同类型的电容器在实际应用中有那些具体差异 2.2.3、如何选择合适的电容器来满…

OrangePi AIpro 香橙派 昇腾 Ascend C 算子开发 与 调用 - Tiling实现 2

OrangePi AIpro 香橙派 昇腾 Ascend C 算子开发 与 调用 - Tiling实现 2 flyfish 前置知识 1 前置知识 2 Host侧CPU和Device侧NPU的主要区别 不同的硬件资源 CPU是为了执行通用计算任务而设计的&#xff0c;但在处理大量的并行计算&#xff08;如矩阵乘、批数据处理&#…

智能优化算法-北方苍鹰优化算法(NGO)(附源码)

目录 1.内容介绍 2.部分代码 3.实验结果 4.内容获取 1.内容介绍 北方苍鹰优化算法 (Northern Goshawk Optimizer, NGO) 是一种基于群体智能的元启发式优化算法&#xff0c;它模拟了北方苍鹰&#xff08;Northern Goshawk&#xff09;的捕食行为、领地行为以及社交互动&#x…

input系统之InputDispatcher

往期文章&#xff1a; Input系统之IMS的启动流程 input系统之InputReader 1.概述 InputReader和InputDispatcher是Input系统的重要组成部分&#xff0c;InputReader主要负责从设备节点获取原始输入事件&#xff0c;并将封装好的事件交给InputDispatcher&#xff1b;InputDis…

数据库 | 子查询 | sql执行顺序 | mysql是否运行

1.系统&#xff08;客户端&#xff09;访问 MySQL 服务器前&#xff0c;做 的第一件事就是建立 TCP 连接。 Caches & Buffers&#xff1a; 查询缓存组件SQL Interface: SQL接口 接收用户的SQL命 令&#xff0c;并且返回用户需要查询的结果。比如 SELECT … FROM就是调用SQ…

防止goroutine崩溃导致主进程崩溃

在Go语言中&#xff0c;当一个goroutine发生异常时&#xff0c;它会直接退出&#xff0c;并不会影响其他goroutine或者主进程。Go语言的设计哲学是“不要通过共享内存来通信&#xff0c;而应该通过通信来共享内存”。这就意味着&#xff0c;goroutine之间的协作通常是通过chann…

Azure AI Search 中的二进制量化:优化存储和加快搜索速度

随着组织继续利用生成式 AI 的强大功能来构建检索增强生成 (RAG) 应用程序和代理&#xff0c;对高效、高性能和可扩展解决方案的需求从未如此强烈。 今天&#xff0c;我们很高兴推出二进制量化&#xff0c;这项新功能可将向量大小减少高达 96%&#xff0c;同时将搜索延迟减少高…

2024.9.4 作业

自己实现栈和队列的全类型 代码&#xff1a; /*******************************************/ 文件名&#xff1a;sq.h /*******************************************/ #ifndef SQ_H #define SQ_H #include <iostream> #include <stdexcept> using namespace…

文档一键生成组织架构图,这款在线AI工具让你事半功倍!

在呈现公司组织架构或部门层级时&#xff0c;经常会用到组织架构图&#xff0c;组织架构图是一种直观的可视化工具&#xff0c;用于展示一个组织内部的层级结构和各部门之间的关系。 在制作组织架构图时&#xff0c;如果拿到的是清晰的人员关系&#xff0c;绘制起来还比较简单…

LeetCode 热题100-64 搜索二维矩阵

搜索二维矩阵 给你一个满足下述两条属性的 m x n 整数矩阵&#xff1a; 每行中的整数从左到右按非严格递增顺序排列。每行的第一个整数大于前一行的最后一个整数。 给你一个整数 target &#xff0c;如果 target 在矩阵中&#xff0c;返回 true &#xff1b;否则&#xff0c…

访问Swagger:java.lang.NumberFormatException: For input string: ““

你们好&#xff0c;我是金金金。 场景 启动项目&#xff0c;接着访问Swagger 依赖版本如下&#xff1a;1.5.20 查看控制台输出&#xff1a;报错如下 排查 首先看报错&#xff1a;For input string: “”&#xff1a;这个错误表明程序尝试将一个空字符串&#xff08;“”&#x…

JavaWeb JavaScript 9.正则表达式

生命的价值在于你能够镇静而又激动的欣赏这过程的美丽与悲壮 —— 24.8.31 一、正则表达式简介 正则表达式是描述字符模式的对象。正则表达式用简单的API对字符串模式匹配及检索替换&#xff0c;是对字符串执行模式匹配的强大工具。 1.语法 var pattnew RegExp(pattern,modi…

【qt踩坑】路径含中文导致的报错,以及 OpenGL的链接报错

​ 背景 本来是准备采用VSQt插件的方式来开发Qt的&#xff0c;但是学习过程中发现&#xff0c;这种模式还是没有直接用Qt Creator 开发来的方便&#xff0c;插件这种模式坑多&#xff0c;功能不完善。 不过在直接使用Qt Creator的时候也踩坑了&#xff1a; (最后发现&#x…

【达梦】“6103无效的时间类型值”解决办法

场景 使用DM数据迁移工具将excel文件里的数据导入到达梦数据库里。提示“无效的时间类型值”。 尝试 一看就是createTime等跟时间相关的字段出问题了。createTime在库里的数据类型为timeStamp。 尝试1&#xff1a;修改excel里此字段的类型&#xff0c;依旧报错。此方案失败。…

单向链表与双向链表

当使用单向链表查看链表中某个节点的数据&#xff0c;可以使用快慢指针法 快慢指针&#xff1a; 快慢指针是一种在链表和数组中常用的算法技巧&#xff0c;主要用于解决链表或数组中的问题&#xff0c;如检测环 存在、找到环的入口、计算链表的中点等。快慢指针的核心思想是…