当面试中问到关于多线程安全问题时,你还不知道怎么回答嘛?快点进来,我带你多维度、深层次来解决这个问题

2023-11-02

1、synchronized:

synchronized 关键字:线程安全用的,同步关键字

(1)作用:

对象头加锁,同一个对象加锁的线程 同步互斥

(2)语法 / 使用:

(注意:只有对 同一对象 加锁,才会让线程产生同步互斥的作用)
(可重入性:同一个线程可以对同一个对象锁多次申请)

  • 1、同步代码块:( synchronized(某个对象){…} )
public class SynchronizedTest {
    public static void increment() {
    }
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (SynchronizedTest.class) {
                    increment();
                }
            }
        }).start();
    }
}

在这里插入图片描述

  • 2、实例同步方法:(synchronized(this))
public class SynchronizedDemo {
    public synchronized void methond() {
    }
    public static void main(String[] args) { 
        SynchronizedDemo demo = new SynchronizedDemo();
        // 进入方法会锁 demo 指向对象中的锁;出方法会释放 demo 指向的对象中的锁
        demo.method();  
    } 
}
  • 3、静态同步方法:(synchronized(当前类.class))
public class SynchronizedDemo { 
    public synchronized static void methond() {
    }
    public static void main(String[] args) { 
    // 进入方法会锁 SynchronizedDemo.class 指向对象中的锁;出方法会释放 SynchronizedDemo.class 指向的对象中的锁
        method();
    } 
}

(3)原理 / 底层实现:

1、原理

多个线程间同步互斥(一段代码在任意一个时间点,只有一个线程执行:加锁、释放锁)
( 加锁/释放锁:基于对象

2、底层实现

对象锁(monitor)机制:
在这里插入图片描述

  • (1)基于 monitor 对象的监视器:使用 对象头的锁状态 来加锁
  • (2)编译为 字节码指令1个 monitorenter + 2个 monitorexit
    (为什么后面是两个? 多出来的是因为 catch 异常也需要释放对象锁的情况)
  • (3)monitor 存在 计数器 实现 synchronized 的 可重入性:进入+1,退出-1

(4)JVM 对 synchronized 的优化:

JVM 将 synchronized 锁分为4种,级别 从低到高 依次是:无锁、偏向锁、轻量级锁、重量级锁(会根据情况,进行依次升级)

锁只能升级却不能降级,目的是为了提高获得锁和释放锁的效率(基于对象头的锁状态来实现)

  • 无锁:没有对资源进行锁定,所有的线程都能访问并修改同一个资源,但同时只有一个线程能修改成功,其他修改失败的线程会不断重试直到修改成功
  • 偏向锁:同一个对象多次加锁(可重入性)
    偏向第一个加锁线程,该线程是不会主动释放偏向锁的,只有当其他线程尝试竞争偏向锁才会被释放
    偏向锁的撤销:需要在某个时间点上没有字节码正在执行时,先暂停拥有偏向锁的线程,然后判断锁对象是否处于被锁定状态。如果线程不处于活动状态,则将对象头设置成无锁状态,并撤销偏向锁;
    (如果线程处于活动状态,升级为轻量级锁的状态)
  • 轻量级锁:基于 CAS 实现,同一个时间点,经常只有一个线程竞争
    指当锁是偏向锁的时候,被第二个线程 B 所访问,此时偏向锁就会升级为轻量级锁,线程 B会通过自旋的形式尝试获取锁,线程不会阻塞,从而提高性能
  • 重量级锁:基于系统的互斥锁 mutex 锁(当有一个线程获得锁后,其他线程阻塞)
    重量级锁通过对象内部的监视器(monitor)实现,而其中 monitor 的本质是依赖于底层操作系统的 Mutex Lock 实现,操作系统实现线程之间的切换需要从用户态切换到内核态,切换成本非常高

其他优化:

  • 粗优化:是将多次连接在一起的加锁、解锁操作合并为一次,将多个连续的锁扩展成为一个范围更大的锁
public class Test{
    private static StringBuffer sb = new StringBuffer();
    public static void main(String[] args) {
        sb.append("a");
        sb.append("b");
        sb.append("c");
   }
}

这里每次调用 stringBuffer.append 方法都需要加锁和解锁,如果虚拟机检测到有一系列连串的对同一个对象加锁和解锁操作,就会将其合并成一次范围更大的加锁和解锁操作,即在第一次append方法时进行加锁,最后一次append方法结束后进行解锁。

  • 锁消除:删除不必要的加锁操作
    根据代码逃逸技术,如果判断到一段代码中,堆上的数据不会逃逸出当前线程,那么可以认为这段代码是线程安全的,不必要加锁
public class Test{
    public static void main(String[] args) {
        StringBuffer sb = new StringBuffer();
        sb.append("a").append("b").append("c");
   }
}

虽然StringBuffer的append是一个同步方法,但是这段程序中的StringBuffer属于一个局部变量,并且不会从该方法中逃逸出去,所以其实这过程是线程安全的,可以将锁消除

2、Lock体系:

(1)为什么会有Lock体系(是为了死锁的解决)

Java 的 1.5 版本中,synchronized 性能不如 SDK 里面的Lock,但 1.6 版本之后,synchronized 做了很多优化,将性能追了上来,所以 1.6 之后的版本又有人推荐使用 synchronized 了
(SDK:软件开发工具包,开发中是一个工程提供另一个工程接口(JDK 只是他的一个子集))

synchronized 在处理死锁问题时方法非常局限,只能避免资源上锁线性化,除此之外基本别无他法。而下面要介绍的Lock可以有很多种办法来避免死锁的产生:

死锁的产生条件:

  • 1、互斥,共享资源 X 和 Y 只能被一个线程占用
  • 2、占有且等待,线程 T1 已经取得共享资源 X,在等待共享资源 Y 的时候,不释放共享资源 X
  • 3、不可抢占,其他线程不能强行抢占线程 T1 占有的资源
  • 4、循环等待,线程 T1 等待线程 T2 占有的资源,线程 T2 等待线程 T1 占有的资源,就是循环等待

解决死锁: 只要我们破坏其中一个,就可以成功避免死锁的发生

(其中,互斥这个条件我们没有办法破坏,因为我们用锁为的就是互斥)

  • 1、对于“占用且等待”这个条件,我们可以一次性申请所有的资源,这样就不存在等待了
  • 2、对于“不可抢占”这个条件,占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占有的资源,这样不可抢占这个条件就破坏掉了
  • 3、对于“循环等待”这个条件,可以靠按序申请资源来预防。所谓按序申请,是指资源是有线性顺序的,申请的时候可以先申请资源序号小的,再申请资源序号大的,这样线性化后自然就不存在循环了

synchronized 无法做到第二点 , 原因是 synchronized 申请资源的时候,如果申请不到,线程直接进入阻塞状态了,而线程进入阻塞状态,啥都干不了,也释放不了线程已经占有的资源

进一步设计 破坏不可抢占条件:

  • 1、能够 响应中断。给阻塞的线程发送中断信号的时候,能够唤醒它
  • 2、支持超时。如果线程在一段时间之内没有获取到锁,不是进入阻塞状态,而是返回一个错误
  • 3、非阻塞地获取锁。如果尝试获取锁失败,并不进入阻塞状态,而是直接返回

这三种方法可以全面弥补 synchronized 的问题,体现在 API 上,就是 Lock 接口的三个方法

// 支持中断的 API
void lockInterruptibly() throws InterruptedException;
// 支持非阻塞获取锁的 API
boolean tryLock();
// 支持超时的 API
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

(2)简介:

使用上 : synchronized 是基于对象头加锁来实现。。而 lock 本身就是锁
(语法上都可以相互转变)
在这里插入图片描述
原理:Lock 基于 AQS 独占锁实现,多个线程申请锁,基于 CAS 设置线程的同步状态。
成功:获取锁向下执行;失败:线程放下 AQS 队列(阻塞)

(3)AQS

(AbstractQueuedSynchronizer 抽象的队列式的同步器)

同步队列,独占式锁的获取和释放,共享锁的获取和释放以及可中断锁,超时等待锁获取这些特性的实现
在这里插入图片描述

AQS实际上通过头尾指针来管理同步队列,同时实现包括获取锁失败的线程进行入队,释放锁时对同步队列中的线程进行通知等核心方法
在这里插入图片描述

(4)Reentrantlock:可重入锁,实现Lock

提供了 公平锁 和 非公平锁(两种锁都是基于 AQS 来实现)

  • 公平锁:满足时间上的顺序(队列 FIFO)(排队的方式)
  • 非公平锁:随机性(可能的问题:某个线程运气不好,一直获取不到锁而阻塞)
    但性能更高,无参构造方法默认以非公平锁创建(划拳的方式)

(5)ReentrantReadWriteLock:读写锁

  • readLock(): 返回读锁
  • writeLock():返回写锁

特点:读读并发,写写 / 读写互斥

使用场景:例如:Tomcat多个 http 请求(多个线程),对服务端本地一个文件读写操作,允许读读并发。相比 ReentrantLock 或是 synchronized ,效率更高

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

当面试中问到关于多线程安全问题时,你还不知道怎么回答嘛?快点进来,我带你多维度、深层次来解决这个问题 的相关文章

  • Anaconda 安装 Python 库(MySQLdb)的方法-(转)

    安装python库的过程中 最重要的地方就是版本需要兼容 其中操作系统为64位 Python为2 X 64位 下载安装文件的时候也要注意版本匹配 其中文件名中包含的cp27表示CPython 2 7版本 cp34表示CPython 3 4
  • 操作系统常见面试题

    1 什么是进程 Process 和线程 Thread 有何区别 进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动 进程是系统进行资源分配和调度的一个独立单位 线程是进程的一个实体 是CPU调度和分派的基本单位 它是比进程更小的能
  • 磁盘调度算法笔记和练习题

    磁盘调度算法 先来先服务FCFS 最短寻道时间优先SSTF 扫描调度SCAN 练习题 先来先服务FCFS 最短寻道时间优先SSTF 扫描调度SCAN 它是一次只响应一个方向上的请求 这个方向上的请求都响应完了 再掉头处理另一个方向上的 有点
  • MacOS中清除原有ssh公钥方法

    2019独角兽企业重金招聘Python工程师标准 gt gt gt 用ssh的跳转登录服务器后 ssh会把你每个你访问过计算机的公钥 public key 都记录在 ssh known hosts 当下次访问相同计算机时 SSH会核对公钥
  • 同步单例的正确使用?

    所以我正在考虑建立一个业余爱好项目 这是一种临时性的事情 只是为了温习我的编程 设计 它基本上是一个多线程网络蜘蛛 更新相同的数据结构object gt int 因此 为此使用数据库绝对是大材小用 我唯一能想到的是用于包含我的数据结构的线程
  • 在 Java 的 main() 中对 Thread 实例运行 wait()

    我正在尝试 java lang Object 中 wait 的定时版本 并观察到它在两种不同场景中的行为有所不同 场景1 使用Thread中run 的默认定义 public static void main String args thro
  • 使用“notify()”和“wait()”代替“suspend()”和“resume()”来控制线程

    我正在尝试学习如何在java中暂停和恢复线程 我正在使用一个Applet that implements Runnable有2个按钮 开始 和 停止 public void init th new Thread this th start
  • 在Java中,如何测试对象的监视器是否被锁定? [复制]

    这个问题在这里已经有答案了 在Java中 如何测试对象的监视器是否被锁定 换句话说 给定一个对象 obj 是否有任何线程拥有 obj 的监视器 我不关心哪个线程拥有监视器 我需要测试的是是否有任何线程拥有给定对象的监视器 由于当前线程以外的
  • Java围绕参数值同步方法

    考虑以下方法 public void upsert int customerId int somethingElse some code which is prone to race conditions 我想保护此方法免受竞争条件的影响
  • Java 中使用同步块的并发未给出预期结果

    下面是一个简单的 java 程序 它有一个名为 cnt 的计数器 该计数器会递增 然后添加到名为 monitor 的列表中 cnt 由多个线程递增 值由多个线程添加到 monitor 在方法 go 的末尾 cnt 和 monitor siz
  • 为什么同步字段变量并在同步块内递增它会导致打印乱序?

    我有一个简单的代码片段 public class ItemManager private Integer itemCount 0 public void incrementAndPrint synchronized this System
  • 在lockObject上同步和使用this作为锁有什么区别?

    我知道同步方法和同步块之间的区别 但我不确定同步块部分 假设我有这个代码 class Test private int x 0 private Object lockObject new Object public void incBloc
  • 当锁持有非最终对象时,该对象的引用是否仍然可以被另一个线程更改?

    当一个对象需要同步时 如果它没有设置为非最终的 IDE 会抱怨 因为它的引用不是持久的 private static Object myTable synchronized myTable IDE complains access myTa
  • 当我们使用synchronized关键字时,什么会被锁定? [复制]

    这个问题在这里已经有答案了 在阅读线程中的并发问题并通过以下方式处理它时 我想到了问题synchronized关键词是 当我们使用这个术语时lock它用于包含以下内容的对象run方法 或线程的工作 但是为什么我们不能使用这个术语lock仅适
  • 为什么 Java 中的同步成本很高?

    我对 Java 很陌生 我读到了synchronized在 Java 中 非常昂贵 我只想知道什么贵 怎么贵 Thanks 也许事情并没有你想象的那么糟糕 http www coderanch com t 462449 Threads Sy
  • 如何正确使用同步链接哈希图

    尝试通过子类化链接哈希映射来制作 lru 映射 地图通过 collections synchronized 运行 映射的所有用法都被同步块包围 如果它们全部被删除 单元测试也会失败 人们可能会认为它们是不必要的 因为地图是通过 collec
  • 易失性数组的替代方案

    从其他问题中 我了解到易失性数组的元素不是易失性的 只有引用本身是不稳定的 volatile int data Thread A data 4 457 Thread B System out println data 4 在这里 线程 B
  • 【操作系统xv6】学习记录4-一级页表与二级页表

    占位
  • 同步块——锁定多个对象

    我正在建模一个游戏 其中多个玩家 线程 同时移动 玩家当前所在位置的信息被存储两次 玩家有一个变量 hostField 它引用棋盘上的一个字段 每个字段都有一个 ArrayList 存储当前位于该字段的玩家 我对拥有冗余信息这一事实不太满意
  • 异步映射中的同步部分

    我有一个大的 IO 函数 它将持续从文件夹加载数据 对数据执行纯计算 然后写回 我正在多个文件夹上并行运行此函数 mapConcurrently iofun folderList from http hackage haskell org

随机推荐

  • microPython在STM32跑一跑

    1 看这个文章在STM32F4 Disco上试用MicroPython MicroPython开源版块 电子工程世界 论坛 于是在DISCOVERY板卡上面代码就能跑起来了 灯可以闪烁 让灯闪烁的代码可以通过串口输入进去 也可以通过通过生成
  • 【TCP/IP】 以太网流量控制------pause流控

    关键字 以太网 数据链路层 PAUSE帧 流量控制 文章目录 关键字 一 以太网的流量控制 二 pause流控的原理和实现 1 pause流控原理 2 pause消息格式 3 pause流控处理逻辑 4 pause流控芯片上的实现 三 pa
  • 网络层设备 —— 路由器

    一 路由器 二 输入端口对线路上收到的分组的处理 三 输出端口将交换结构传送来的分组发送到线路 四 路由表和转发表 1 路由表 根据路由选择算法得出 主要用途是路由选择 其结构需
  • QObject类的对象树机制、qt内存回收机制、deleteLater、 delete与ui关系

    catalog 错误范例 version qt的delete deleteLater机制 deleteLater具体使用 delete与ui关系 错误范例 class MyWidget QWidget QPushButton btn QTa
  • mysql常见面试题

    一 联合索引是什么 为什么需要注意联合索引中的顺序 什么是最左前缀匹配原则 MySQL可以使用多个字段同时建立一个索引 叫做联合索引 在联合索引中 如果想要命中索引 需要按照建立索引时的字段顺序挨个使用 否则无法命中索引 最左前缀匹配原则
  • windows下将tomcat设置为系统服务以便自动启动

    在window sever服务器部署程序的时候 最好是将程序设置为服务 单纯用cmd启动可能会面临被误关掉的风险 写下此日志留作教训 添加为服务 第一步 使用cmd命令进入到tomcat文件下的bin目录 第二步 输入service ins
  • 52 最佳实践-安全最佳实践-sVirt保护

    文章目录 52 最佳实践 安全最佳实践 sVirt保护 52 1 概述 52 2 开启sVirt保护 52 2 1 开启主机的SELinux 52 2 2 创建开启sVirt功能的虚拟机 52 2 3 确认sVirt开启成功 52 最佳实践
  • Blender interface界面介绍

    Blender interface界面介绍 Blender的主界面 cycles 渲染引擎 有噪点 但是他的渲染效果会很好 因为他是光线追踪 模拟真实的漫反射 实时渲染 evee 2 08过后软件才出的 渲染图层 场景 世界 其中 会用到很
  • 测试理论基础学习

    软件测试概述 一 软件测试的定义 软件测试是为了发现错误而针对某个程序或系统的执行过程 其目的在于检验它是否满足规定的需求 二 软件测试的原则 1 穷举测试是不可能的 2 测试用例的设计是关键 3 缺陷具有免疫性 测试用例要不断更新 4 对
  • Exchange2010/2013/2016删除指定主题邮件

    Exchange2010 2013 2016删除指定主题邮件 说到Exchange服务 大家并不陌生了 但是作为一个Exchange管理员的话 随时都在学习中 近期有个问题 用户发错邮件了 说能不能把用户收到的主题邮件进行删除 所以就同意帮
  • Hyperledger Fabric文档v2.2(四)

    入门 参考博文 搭建 Fabric 测试网络 在我们开始之前 如果您还没有这样做 您可能希望检查您是否已经在将要开发区块链应用程序或运行 Hyperledger Fabric 的平台上安装了所有 准备阶段 安装必备组件后 即可下载并安装 H
  • HashSet、LinkedHashSet、TreeSet使用区别

    HashSet 哈希表是通过使用称为散列法的机制来存储信息的 元素并没有以某种特定顺序来存放 LinkedHashSet 以元素插入的顺序来维护集合的链接表 允许以插入的顺序在集合中迭代 TreeSet 提供一个使用树结构存储Set接口的实
  • Cobalt Strike(CS)介绍,安装及初步使用(学习笔记1)

    目录 CS简介 2 安装使用 CS简介 Cobalt Strike 简称CS 是一个为对手模拟和红队行动而设计的平台 主要用于执行有目标的攻击和模拟高级威胁者的后渗透行动 分为服务器和客户端 模式 Cobalt Strike使用C S架构
  • 微信小程序-入门篇(app.wxml及app.wxss)

    app wxml相当于我们的app中的xml文件内容 看了大半天的界面代码感觉还是不太适应这种js风格的东西 不过以后了解多了估计就好些了 我们首先介绍 wxml内容 有句文档上的话很重要 WXML 中的动态数据均来自对应 Page 的 d
  • 国际化动态加载数据

    国际化动态加载数据 起因 公司业务涉及比较广泛 所做网页必然要涉及到国际化 国际化本是老生常谈的内容 但是我们大部分做的都是静态数据国际化 或者联网插件国际化 而很多公司 属于内网开发 故而只能静态数据 但是整个项目很大 很多时候静态数据太
  • win10通过命令或通过控制面板关闭防火墙

    1 点击控制面板 Win键 R 输入control enter 点击防火墙 打开或关闭防火墙 关闭防火墙 2 通过命令关闭防火墙 关闭防火墙 netsh firewall set opmode mode disable 查看防火墙状态 Ne
  • 总结之Linux环境(一)——命令行

    从一开始学习linux到现在 已经有很多时间没有碰linux系统了 这个陌生感让人很不适 以前也没有写什么总结 再接触还是也个总结对以后使用根据方便 搬运某培训机构资料 又快又全 Linux 的五个重启命令 1 shutdown shutd
  • c++11多线程编程(十):packaged_task<>介绍与实例

    本节讨论c 11中std packaged task的特性与使用方法 std packaged task lt gt std packaged task lt gt 是一个类模板 代表一个异步任务 封装了 1 可调用实体 即函数 lambd
  • [深入研究4G/5G/6G专题-31]: 5G NR开机流程5.2 - UE Attach流程(Registeration Request/Accept/Complete)

    作者主页 文火冰糖的硅基工坊 文火冰糖 王文兵 的博客 文火冰糖的硅基工坊 CSDN博客 本文网址 https blog csdn net HiWangWenBing article details 125160897 目录 前言 前置条件
  • 当面试中问到关于多线程安全问题时,你还不知道怎么回答嘛?快点进来,我带你多维度、深层次来解决这个问题

    保证线程安全 1 synchronized 1 作用 2 语法 使用 3 原理 底层实现 4 JVM 对 synchronized 的优化 2 Lock体系 1 为什么会有Lock体系 是为了死锁的解决 2 简介 3 AQS 4 Reent