首页 > 其他分享 >synchronized

synchronized

时间:2024-08-01 13:41:17浏览次数:16  
标签:加锁 操作系统 synchronized 线程 自旋 id

要想理解 synchronized 原理,必须要了解 cas 和 用户态、内核态的理论

synchronized 是关键字,具体怎么实现要翻 cpp、汇编代码,记住理论就行了

CAS

全称叫 Compare And Swap 或者 Compare And Set,比较并交换、比较并设置。具体是:在执行操作之前,先比较当前内存中的值是否等于期望值,如果相等,则执行修改操作;如果不相等,则不执行修改操作,继续进行比较,直到内存中的值与期望值相等为止

举个例子,比如变量 a = 1,要做 a++ 操作

  1. 线程读取 a 的值,保存在自己的栈内(每个线程有自己的栈空间,变量的值保存在 cpu 寄存器)
  2. 现在已经拿到 a 的值了,做 a++,结果是 2
  3. 把 2 回写到 变量 a 中,不是直接回写,先再次读取a的值,判断是否符合条件才决定是否回写
    1. 再次读取 a 的值还是1(说明没有别的线程修改),就把2回写
    2. 如果再次读取的值不是1了,说明别的a已经被别的线程修改了,2就不会回写
  4. 如果重新读取的a不是1,重新做a++,重做的顺序和上面一样,直到成功+1

这个过程是有问题的,比较经典的是 ABA 问题,这个可以通过版本号解决

除了 ABA 还有个更深入的问题,a 一开始是1,a++ 做完,对比 a,然后把新的值写入 a 的过程中,对比和写入是必须保证原子性的,这条命令不能被打断,这是怎么保证的?java 调用 c++,c++ 调用汇编,给这条指令上了个锁(要证明就要去翻 jvm 的 cpp 代码,还要能看懂汇编代码)

用户态、内核态

这是操作系统的两种运行模式,不同模式有不同命令,内核态的命令一般程序不能直接调用执行,需要经过操作系统,操作系统执行这些高级别的命令。JVM 是已用户态的模式在运行,想要加一把重量级锁,这个只能用户态跟操作系统发起申请,操作系统接收到指令然后调用内核态的指令,这个开销是比较大的

比如 java 不能直接操作内存,只能间接通过 UnSafe 类访问,比如前端 js 不能操作系统文件,再比如普通程序不能直接修改操作系统内核结构等等

synchronized

加锁的方式就不说了,同步方法,同步代码块等

synchronized 可以给类加锁,可以给对象加锁,究竟是怎么加的?修改对象头信息,修改成功就表示加锁成功,可以使用 JOL 分析对象内存分布

jvm 启动 4 秒后开启偏向锁,可以在同步代码块中对一个对象加锁(比如 obj),程序启动在主方法中使用 JOL 分析对象内存分布,休眠5秒,然后调用同步代码块的方法,再使用 JOL 打印内存分布,会发现两次答应的对象头不一样

也可以配置延迟时间为0,jvm 一启动就开启偏向锁,锁升级过程如下

  1. 创建对象 obj,这时是无锁,对象头是 obj 的信息,没有和锁相关的信息
  2. 遇到 synchronized 时,加锁,这时加的是偏向锁
    1. 具体是对象头里 markword 的线程id改为当前持有锁的线程 id
    2. 对象头有哪个线程id,就表示这个对象被哪个线程持有锁
    3. 后续如果有重入或竞争先对比线程 id,如果 id 一致,没有竞争,一直是偏向锁
  3. 如果有线程竞争,升级为自旋锁
    1. 也叫轻量级锁、无锁(这个无锁指的是没加重量级锁),CAS 实现,别的线程一直自旋等待
    2. 具体是别的线程一直CAS自旋尝试将对象头的 markword 的线程 id 设置成自己的,如果设置成功就表示持有锁了
  4. 如果竞争加剧,升级为重量级锁
    1. 哪种条件视为竞争加剧?
      1. jdk1.6及以前:自旋次数超过10次,或自选线程数量超过cpu核数一半,自选次数可配置,--XX:PreBlockSpin
      2. jdk1.6 之后:由 JVM 自己判断自旋次数、自旋线程数是多少时视为竞争加剧
    2. 和内核态通信,向操作系统申请资源

为什么有轻量级锁还要重量级锁?线程自旋是要消耗 CPU 的,当线程数量过多显然不适合继续自旋。重量级锁会把这些等待的线程放进一个队列,这个队列里面的线程不会消耗 CPU

标签:加锁,操作系统,synchronized,线程,自旋,id
From: https://www.cnblogs.com/cyrushuang/p/18336488

相关文章

  • 当@Transactional遇上@synchronized生产问题
    近日遇到一个问题,就是一个订单被两个用户抢了问题,排查后发现是由于@Transactional和@synchronized注解的使用问题一、问题点:数据重复读@Transactional注解用于开启事务,当在高并发情况下我们可能为了保证数据的安全使用悲观锁,可以在方法上使用@synchronized使用悲观锁。一......
  • synchronized关键字
    在Java中,关键字synchronized可以保证在同一个时刻,只有一个线程可以执行某个方法或者某个代码块(主要是对方法或者代码块中存在共享数据的操作),同时我们还应该注意到synchronized的另外一个重要的作用,synchronized可保证一个线程的变化(主要是共享数据的变化)被其他线程......
  • Synchronized
    Synchronized的表现形式普通同步方法普通同步方法使用 synchronized 关键字修饰,锁对象是当前实例对象(即方法所属对象的实例)。publicsynchronizedvoidmethod(){//锁对象是当前实例对象}静态同步方法静态同步方法使用 synchronized 关键字修饰,锁对象是当前......
  • [ABC339D]Synchronized Players
    题目大意给定一个N*N的地图,地图中#表示不能走,地图上有两个人,两个人每次走的方向都相同,每次只能向相同的方向走,,问最少几步就能相遇?题解这个题一看就是搜索,广搜,刚开始我陷入一个误区,让两个人分开记录状态,这个时候有个问题,当一个人能走,另一个人不能走的时候,步数就不同步,这个问题就......
  • 【转】-synchronized与Lock的区别与使用
    详解synchronized与Lock的区别与使用该博客转载自​淳安郭富城​的​详解synchronized与Lock的区别与使用1.引言:昨天在学习别人分享的面试经验时,看到Lock的使用。想起自己在上次面试也遇到了synchronized与Lock的区别与使用。于是,我整理了两者的区别和使用情况,同时,对synchroni......
  • [Java并发]Synchronized
    publicclassAtomicTest01{publicstaticinti=0;publicstaticvoidmain(String[]args){Runnabletask=newRunnable(){@Overridepublicvoidrun(){synchronized(this){tr......
  • 关于锁的使用,千万不要踩这个坑!(附带Synchronized详解和ZooKeeper、Redis等分布式锁详解
    1、分布式锁在分布式系统中,我们经常会使用各种锁来保证数据的一致性和并发安全。一些常见的分布式锁实现包括:基于ZooKeeper的分布式锁:使用ZooKeeper节点的特性来实现分布式锁。基于Redis的分布式锁:利用Redis的原子性操作和过期时间特性来实现分布式锁。Redlock算法:由......
  • synchronized 和 ReentrantLock的区别
    synchronized和ReentrantLock的区别  在讨论synchronized和ReentrantLock的区别前,我们先了解一下什么是公平锁和非公平锁  一、公平锁和非公平锁  从公平的角度来说,Java中的锁总共可分为两类:公平锁和非公平锁。但公平锁和非公平锁有哪些区别?孰优孰劣呢?在Java......
  • synchronized 和 ReentrantLock (Lock)区别,优劣对比
    两种方法都是为了确保多线程环境中的线程安全,但它们使用了不同的同步机制:synchronized关键字和Lock接口。下面详细对比这两种方法的区别、优缺点以及适用场景。synchronized关键字publicsynchronizedvoidaddSession(HttpSessionsession){if(session!=null){......
  • java synchronized 保护线程安全
    前言工作中自己实现了一个MySessionContext类,在实现addSession方法的时候,考虑到会有线程不安全问题,这里需要使用synchronized关键字来保护线程安全。理解synchronized关键字需要了解多线程和线程安全的基本概念。在多线程环境中,多个线程可以同时访问共享资源(例如内存中的变量......