多线程常见问题集

一、多线程预防和避免线程死锁

如何预防死锁? 破坏死锁的产生的必要条件即可:

  1. 破坏请求与保持条件:一次性申请所有的资源。
  2. 破坏不剥夺条件:占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占有的资源。
  3. 破坏循环等待条件:靠按序申请资源来预防。按某一顺序申请资源,释放资源则反序释放。破坏循环等待条件。

如何避免死锁?

避免死锁就是在资源分配时,借助于算法(比如银行家算法)对资源分配进行计算评估,使其进入安全状态。

安全状态 指的是系统能够按照某种线程推进顺序(P1、P2、P3……Pn)来为每个线程分配所需资源,直到满足每个线程对资源的最大需求,使每个线程都可顺利完成。称 <P1、P2、P3.....Pn> 序列为安全序列

二、简述CAS

CAS的全称是 CompareAndSwap(比较与交换) ,用于实现乐观锁,被广泛应用于各大框架中。CAS的思想很简单,就是用一个预期值和要更新的变量值进行比较,两值相等才会进行更新。

CAS是一个原子操作,底层依赖于一条CPU的原子指令。

原子操作 即最小不可拆分的操作,也就是说操作一旦开始,就不能被打断,直到操作完成。

CAS涉及到三个操作数:

  • V:要更新的变量值(Var)
  • E:预期值(Expected)
  • N:拟写入的新值(New)

当且仅当V的值等于E时,CAS通过原子方式用新值N来更新V的值。如果不等,说明已经有其它线程更新了V,则当前线程放弃更新。

举一个简单的例子:线程A要修改变量i的值为6,i原值为1(V=1,E=1,N=6,假设不存在ABA问题)。

i与1进行比较,如果相等,则说明没被其他线程修改,可以被设置为6。

i与1进行比较,如果不相等,则说明被其他线程修改,当前线程放弃更新,CAS操作失败。

当多个线程同时使用CAS操作一个变量时,只有一个会胜出,并成功更新,其余均会失败,但失败的线程并不会被挂起,仅是被告知失败,并且允许再次尝试,当然也允许失败的线程放弃操作。

Java语言并没有直接实现CAS,CAS相关的实现是通过C++内联汇编的形式实现的(JNI调用)。因此,CAS的具体实现和操作系统以及CPU都有关系。

sun.misc包下的Unsafe类提供了compareAndSwapObject、compareAndSwapInt、compareAndSwapLong方法来实现的对Object、int、long类型的CAS操作

/** @param 包含要修改field的对象*  @param offset    对象中某field的偏移量*  @param expected  期望值*  @param update    更新值*  @return true | false**/
public final native boolean compareAndSwapObject(Object o, long offset,  Object expected, Object update);public final native boolean compareAndSwapInt(Object o, long offset, int expected,int update);public final native boolean compareAndSwapLong(Object o, long offset, long expected, long update)

三、乐观锁和悲观锁

什么是乐观锁?

乐观锁总是假设最好的情况,认为共享资源每次被访问的时候不会出现问题,线程可以不停地执行,无需加锁也无需等待,只是在提交修改的时候去验证对应的资源(也就是数据)是否被其它线程修改了(具体方法可以使用版本号机制或CAS算法)。

在Java中java.util.concurrent.atomic包下面的原子变量类(比如AtomicInteger、LongAdder)就是使用了乐观锁的一种实现方式 CAS 实现的。

// LongAdder 在高并发场景下会比 AtomicInteger 和 AtomicLong 的性能更好// 代价就是会消耗更多的内存空间(空间换时间)LongAdder sum = new LongAdder();sum.increment();

高并发的场景下,乐观锁相比悲观锁来说,不存在锁竞争造成线程阻塞,也不会有死锁的问题,在性能上往往会更胜一筹。但是,如果冲突频繁发生(写占比非常多的情况),会频繁失败和重试,这样同样会非常影响性能,导致CPU飙升。

不过,大量失败重试的问题也是可以解决的,像我们前面提到的 LongAdder以空间换时间的方式就解决了这个问题。

理论上来说:

悲观锁通常多用于写比较多的情况(多写场景,竞争激烈),这样可以避免频繁失败和重试影响性能,悲观锁的开销是固定的。不过,如果乐观锁解决了频繁失败和重试这个问题的话(比如LongAdder),也是可以考虑使用乐观锁的,要视实际情况而定。

乐观锁通常多用于写比较少的情况(多读场景,竞争较少),这样可以避免频繁加锁影响性能。不过,乐观锁主要针对的对象是单个共享变量(参考java.util.concurrent.atomic包下面的原子变量类)。

和数据库乐观锁的区别?

JavaCAS:CAS是底层的原子操作,它适用于线程间对内存中的共享资源进行无锁的、低级别的同步。CAS操作通常用于实现高级的并发数据结构(如无锁队列、栈等)和同步工具(如原子类AtomicInteger、AtomicReference等),这些结构和工具可以在多线程环境下提供更好的性能和更低的延迟。CAS操作不依赖于外部存储,它是在内存级别上保证数据的原子性和一致性。

数据库版本号乐观锁:在数据库操作中,使用版本号作为乐观锁是一种常见的做法。这通常通过在数据表中增加一个版本号字段来实现。每次更新数据时,版本号会递增。在提交更新时,应用程序会检查版本号是否自上次读取以来没有变化。如果版本号匹配,则执行更新;否则,表示数据已被其他事务修改,需要重新获取数据并重试更新操作。这种机制适用于数据库中的高并发更新操作,可以避免因为数据库行锁而导致的性能瓶颈。

什么是悲观锁?

悲观锁总是假设最坏的情况,认为共享资源每次被访问的时候就会出现问题(比如共享数据被修改),所以每次在获取资源操作的时候都会上锁,这样其他线程想拿到这个资源就会阻塞直到锁被上一个持有者释放。也就是说,共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程。

像Java中synchronized和ReentrantLock等独占锁就是悲观锁思想的实现。

高并发的场景下,激烈的锁竞争会造成线程阻塞,大量阻塞线程会导致系统的上下文切换,增加系统的性能开销。并且,悲观锁还可能会存在死锁问题,影响代码的正常运行。

  • 线程池核心参数,线程池工作模型

①、corePoolSize

定义了线程池中的核心线程数量。即使这些线程处于空闲状态,它们也不会被回收。这是线程池保持在等待状态下的线程数。

②、maximumPoolSize

线程池允许的最大线程数量。当工作队列满了之后,线程池会创建新线程来处理任务,直到线程数达到这个最大值。

③、keepAliveTime

非核心线程的空闲存活时间。如果线程池中的线程数量超过了corePoolSize,那么这些多余的线程在空闲时间超过keepAliveTime时会被终止。

④、unit

keepAliveTime参数的时间单位:

TimeUnit.DAYS;天

TimeUnit.HOURS;小时

TimeUnit.MINUTES;分钟

TimeUnit.SECONDS;秒

TimeUnit.MILLISECONDS;毫秒

TimeUnit.MICROSECONDS;微秒

TimeUnit.NANOSECONDS;纳秒

⑤、workQueue

用于存放待处理任务的阻塞队列。当所有核心线程都忙时,新任务会被放在这个队列里等待执行。

⑥、threadFactory

一个创建新线程的工厂。它用于创建线程池中的线程。可以通过自定义 ThreadFactory 来给线程池中的线程设置有意义的名字,或设置优先级等。

⑦、handler

拒绝策略RejectedExecutionHandler,定义了当线程池和工作队列都满了之后对新提交的任务的处理策略。常见的拒绝策略包括抛出异常、直接丢弃、丢弃队列中最老的任务、由提交任务的线程来直接执行任务等。

四、线程池的工作流程

当应用程序提交一个任务时,线程池会根据当前线程的状态和参数决定如何处理这个任务。

如果线程池中的核心线程都在忙,并且线程池未达到最大线程数,新提交的任务会被放入队列中进行等待。

如果任务队列已满,且当前线程数量小于最大线程数,线程池会创建新的线程来处理任务。

空闲的线程会从任务队列中取出任务来执行,当任务执行完毕后,线程并不会立即销毁,而是继续保持在池中等待下一个任务。

当线程空闲时间超出指定时间,且当前线程数量大于核心线程数时,线程会被回收。

public class ThreadPoolDemo {public static void main(String[] args) {// 创建一个线程池ExecutorService threadPool = new ThreadPoolExecutor(3, // 核心线程数6, // 最大线程数0, // 线程空闲时间TimeUnit.SECONDS, // 时间单位new LinkedBlockingQueue<>(10), // 等待队列Executors.defaultThreadFactory(), // 线程工厂new ThreadPoolExecutor.AbortPolicy() // 拒绝策略);// 模拟 10 个顾客来银行办理业务try {for (int i = 1; i <= 10; i++) {final int tempInt = i;threadPool.execute(() -> {System.out.println(Thread.currentThread().getName() + "\t" + "办理业务" + tempInt);});}} catch (Exception e) {e.printStackTrace();} finally {threadPool.shutdown();}}
}

注:此片可能部分摘自JavaGuide  (具体那篇文章链接不记得了从我自己笔记中又拿出来的)

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

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

相关文章

Java ArrayList 与顺序表:在编程海洋中把握数据结构的关键之锚

我的个人主页 我的专栏&#xff1a;Java-数据结构&#xff0c;希望能帮助到大家&#xff01;&#xff01;&#xff01;点赞❤ 收藏❤ 前言&#xff1a;在 Java编程的广袤世界里&#xff0c;数据结构犹如精巧的建筑蓝图&#xff0c;决定着程序在数据处理与存储时的效率、灵活性以…

【第三方云音乐播放器SPlayer本地安装结合内网穿透打造个性化远程音乐库】

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” 文章目录 前言1. 安装Docker2. 创建并启动Splayer容器3. 本地访问测试4. 公网远程访问本地Splayer4.1 内网穿…

easyui combobox 只能选择第一个问题解决

easyui combobox 只能选择第一个问题解决 问题现象 在拆分开票的时候&#xff0c;弹出框上面有一个下拉框用于选择需要新增的明细行&#xff0c;但是每次只能选择到第一个 选择第二条数据的时候默认选择到第一个了 代码如下 /*新增发票编辑窗口*/function addTicketDialog…

从零开始:Linux 环境下的 C/C++ 编译教程

个人主页&#xff1a;chian-ocean 文章专栏 前言&#xff1a; GCC&#xff08;GNU Compiler Collection&#xff09;是一个功能强大的编译器集合&#xff0c;支持多种语言&#xff0c;包括 C 和 C。其中 gcc 用于 C 语言编译&#xff0c;g 专用于 C 编译。 Linux GCC or G的安…

transformer.js(三):底层架构及性能优化指南

Transformer.js 是一个轻量级、功能强大的 JavaScript 库&#xff0c;专注于在浏览器中运行 Transformer 模型&#xff0c;为前端开发者提供了高效实现自然语言处理&#xff08;NLP&#xff09;任务的能力。本文将详细解析 Transformer.js 的底层架构&#xff0c;并提供实用的性…

算法日记 33 day 动态规划(打家劫舍,股票买卖)

今天来看看动态规划的打家劫舍和买卖股票的问题。 上题目&#xff01;&#xff01;&#xff01;&#xff01; 题目&#xff1a;打家劫舍 198. 打家劫舍 - 力扣&#xff08;LeetCode&#xff09; 你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋。每间房内都藏有一定的现金…

STM32 Keil5 attribute 关键字的用法

这篇文章记录一下STM32中attribute的用法。之前做项目的时候产品需要支持远程升级&#xff0c;要求版本只能向上迭代&#xff0c;不支持回退。当时想到的方案是把版本号放到bin文件的头部&#xff0c;设备端收到bin文件的首包部数据后判断是否满足升级要求&#xff0c;这里就可…

wordpress中Gravatar用户头像不显示,免插件实现添加自定义设置上传头像功能

该功能主要是用于免插件为用户添加头像&#xff0c;解决头像无法显示或无法更换自定义头像的问题 虽热这个功能使用场景和频率都非常低&#xff0c;但在有时候还是需要WordPress来显示头像的&#xff0c;但是zuanmang.net并不是每个人都有注册设置Gravatar头像。所以便需要我们…

aws服务--机密数据存储KMS(1)介绍和使用

在AWS(Amazon Web Services)中存储机密数据时,安全性和合规性是最重要的考虑因素。AWS 提供了多个服务和工具,帮助用户确保数据的安全性、机密性以及合规性。AWS Secrets Manager、KMS(Key Management Service)是推荐的存储机密数据的AWS服务和最佳实践。这里先看KMS。 …

51c~C语言~合集2

我自己的原文哦~ https://blog.51cto.com/whaosoft/12652943 一、嵌入式开发中的C语言编译器 如果你和一个优秀的程序员共事&#xff0c;你会发现他对他使用的工具非常熟悉&#xff0c;就像一个画家了解他的画具一样。----比尔.盖茨1 不能简单的认为是个工具 嵌入式程序开发…

ensp静态路由实验

一、实验目的 1、熟练掌握交换机的基本配置命令 2、熟练掌握静态路由的使用方法 3. 熟练掌握交换机端口模式 二、实验内容 需求&#xff1a; 根据要求利用现有实验设备组建小型局域网 实验设备&#xff1a; 交换机S37002台&#xff1b;PC机2台&#xff1b;路由器2台。 …

【git】commit之后,想撤销commit

一、已经commit&#xff0c;想要回退到上一步 保留代码 git reset --soft HEAD^回退到具体的哪一步 HEAD^的意思是上一个版本&#xff0c;也可以写成HEAD~1如果你进行了2次commit&#xff0c;想都撤回&#xff0c;可以使用HEAD~2二、git reflog 查看 sha值 git reflog 回到…

在 Ubuntu 上安装 Yarn 环境

在 Ubuntu 上安装 Yarn 环境 步骤 1: 更新系统步骤 2: 安装 Node.js步骤 3: 安装 Yarn方法 1: 使用 npm 安装方法 2: 使用 APT 安装 步骤 4: 验证安装总结 在 Ubuntu 上安装 Yarn 环境可以通过以下步骤完成&#xff1a; 步骤 1: 更新系统 首先&#xff0c;确保你的系统是最新…

深度学习3

五、自动微分 1、基础概念 模块 autograd 负责自动计算张量操作的梯度&#xff0c;具有自动求导功能&#xff1b;autograd 创建一个动态计算图来跟踪张量的操作&#xff0c;每个张量是计算图中的一个节点&#xff0c;节点之间的操作构成图的边。 属性 requires_grad 决定…

PostgreSQL 中约束Constraints

在 PostgreSQL 中&#xff0c;约束&#xff08;Constraints&#xff09;是用于限制进入数据库表中数据的规则。它们确保数据的准确性和可靠性&#xff0c;通过定义规则来防止无效数据的插入或更新。PostgreSQL 支持多种类型的约束&#xff0c;每种约束都有特定的用途和语法。以…

路由器中继与桥接

一 . 背景 现在的路由器大多数已经开始支持多种网络连接模式&#xff0c;以下将以TP-Link迷你无线路由器为例进行展开介绍。在TP-Link迷你无线路由器上一般有AP&#xff08;接入点&#xff09;模式&#xff0c;Router&#xff08;无线路由&#xff09;模式&#xff0c;Repeate…

2.13 转换矩阵

转换矩阵引用了库nalgebra&#xff0c;使用时研究具体实现。 use std::ops;use nalgebra::Perspective3;use crate::Scalar;use super::{Aabb, LineSegment, Point, Triangle, Vector};/// An affine transform #[repr(C)] #[derive(Debug, Clone, Copy, Default)] pub struct…

SQL进阶:如何跳过多个NULL值取第一个非NULL值?

NULL 一、问题描述二、ORACLE<一>、last_value () over ()<二>、lag () over()<三>、相关子查询 三、MYSQL<一>、全局变量<二>、coalesce() lag() over()<三>、相关子查询<四>、 recursive<五>、lag() over() min() over() …

wordpress获取文章总数、分类总数、tag总数等

在制作wordpress模板的时候会要调用网站的文章总数分类总数tag总数等这个数值&#xff0c;如果直接用count查询数据库那就太过分了。好在wordpress内置了一些标签可以直接获取到这些数值&#xff0c;本文整理了一些常用的wordpress网站总数标签。 文章总数 <?php $count_…

人工智能|计算机视觉——微表情识别(Micro expression recognition)的研究现状

一、简述 微表情是一种特殊的面部表情,与普通的表情相比,微表情主要有以下特点: 持续时间短,通常只有1/25s~1/3s;动作强度低,难以察觉;在无意识状态下产生,通常难以掩饰或伪装;对微表情的分析通常需要在视频中,而普通表情在图像中就可以分析。由于微表情在无意识状态…