Synchronized底层原理

简介: Synchronized底层原理

1.基本使用

synchronized的底层原理:synchronized 是 Java 中的一个关键字,用于控制多线程的访问,确保同一时刻只有一个线程可以进入临界区(被 synchronized 保护的代码块)。

synchronized 的底层实现主要依赖于 JVM 中的 monitor 对象,它是通过对象监视器在对象头中的锁标志位实现的。当一个线程获取到了对象的监视器,那么这个线程就会处于锁定状态,其他尝试获取该监视器的线程将会被阻塞,直到当前线程释放锁。


以下是 synchronized 的使用示例:

public class SynchronizedExample {
    private int count = 0;
 
    public synchronized void increment() {
        count++;
    }
 
    public static void main(String[] args) {
        SynchronizedExample example = new SynchronizedExample();
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    example.increment();
                }
            }).start();
        }
 
        try {
            Thread.sleep(1000); // 等待所有线程执行完毕
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
 
        System.out.println(example.count); // 输出结果应为10000
    }
}

2.Monitor

Monitor被译为监视器,由jvm提供,C++实现。在字节码中想要体现monitor需要借助javap命令查看class字节码。

找到这个类的class文件,在其目录下执行:javap -v SynTest.class 会得到反编译的结果如下:

【重:有两个monitorexit的原因是因为防止锁住的代码抛异常后不能及时释放,所以需要第二个monitorexit】


如果当前monitor的进入数为0时,线程就会进入monitor。synchronize自动释放锁,而Lock必须手动释放,并且代码中出现异常会导致unlock代码不执行,所以Lock一般在Finally中释放,而synchronize释放锁是由JVM自动执行的。


Monitor的内部具体结构:


  • Owner:存储当前获取锁的线程的,只有一个线程可以获取。


  • EntryList:关联没有抢到锁的线程,处于Blocked状态的线程。


  • WaitSet:关联调用了wait方法的线程,处于waiting的线程。


具体流程:


  • 代码进入Synchronized代码块,先让block对象锁关联的monitor,然后判断Owner是否有线程持有。


  • 若没有线程持有,则让当前线程持有,表示该线程获取锁成功。


  • 有现成持有,则让当前线程进入entryList进行阻塞,如果Owner持有的线程已经释放了,在entryList中的线程去竞争锁的持有权(非公平)


  • 若代码调用了wait方法,则会进入waitSet中等待。

3.Synchronized进阶

monitor实现的锁属于重量级的锁,里面涉及了用户态和进程态的切换、进程的上下文切换,成本较高,性能较低。

JDK6引入了两种新型锁:偏向锁和轻量级锁,引入是为了解决在没有多线程竞争或者基本没竞争的场景下因使用传统机制的锁带来的性能开销。

对象的内存结构:

可以通过lock标识,判断锁的等级:

  • 后三位是001表示无锁
  • 后三位是101表示偏向锁
  • 后两位是00表示轻量级锁
  • 后两位是10表示重量级锁

4.锁对比

  • monitor重量级锁:每个对象的对象头都可以设置monitor指针,让对象与之产生关联。里面涉及了用户态和进程态的切换、进程的上下文切换,成本较高,性能较低。
  • 轻量级锁:在没有竞争时,每次重入仍然需要执行CAS操作,保证原子性。线程加锁的时间是错开的,可以用轻量级锁优化,其修改了对象头的锁标志。
  • 偏向锁:只有第一次使用CAS将线程ID设置到对象的Mark Word头,之后发现这个线程ID本身就表示没有竞争,不用重新CAS。以后只要不发生竞争,这个对象就归该线程所有。


CAS(Compare And Swap):比较再交换,体现的是一种乐观锁的思想,在无锁的情况下保证线程操作共享数据的原子性。JUC包下实现的很多类都用上了CAS【AQS框架、AtomicXXXX类】


AQS:AbstractQueuedSynchronizer(队列同步器),AQS定义了一套多线程访问共享资源 的同步器框架,是一个依赖状态(state)的同步器。


  • CAS是基于乐观锁的思想:最乐观的估计,不怕别的线程来修改共享变量,就算改了也没关系。


  • Synchronized是基于悲观锁的思想:最悲观的估计,防止其他线程来修改共享变量,上了锁后不许修改,释放后才能修改。


相关文章
Web应用基本架构
Web应用基本架构。
174 6
|
存储 Shell Linux
【Shell 命令集合 文件管理】Linux 读取命令 read命令使用指南
【Shell 命令集合 文件管理】Linux 读取命令 read命令使用指南
227 0
|
Kubernetes 负载均衡 应用服务中间件
【K8S系列】第十三讲:Ingress详解
【K8S系列】第十三讲:Ingress详解
6629 0
|
11月前
|
安全 Java
如何在 Java 中停止线程
【8月更文挑战第22天】
404 4
|
人工智能 分布式计算 DataWorks
首批!阿里云 MaxCompute 完成中国信通院数据智能平台专项测试
2024年5月31日,在中国信通院组织的首批数据智能平台专项测试中,阿里云数据智能平台解决方案(MaxCompute、DataWorks、PAI)顺利完成测试。
530 5
首批!阿里云 MaxCompute 完成中国信通院数据智能平台专项测试
|
关系型数据库 分布式数据库 数据库
VLDB顶会论文解读 | PolarDB MySQL高性能强一致集群核心技术详解
在VLDB2023会议上,阿里云瑶池数据库团队的论文介绍了PolarDB-SCC,这是一个创新的云原生数据库系统,确保了低延迟的全局强一致读取。PolarDB-SCC解决了传统主从架构中只读节点可能返回过期数据的问题,实现了在不影响性能的情况下提供强一致性。通过重新设计的主从信息同步机制、线性Lamport时间戳和细粒度修改跟踪,以及利用RDMA优化的日志传输,PolarDB-SCC已经在PolarDB中成功应用超过一年,成为业界首个无感知全局一致性读的云原生数据库解决方案。
67339 0
|
9月前
|
SQL 移动开发 Oracle
SQL语句实现查询连续六天数据的方法与技巧
在数据库查询中,有时需要筛选出符合特定时间连续性条件的数据记录
|
9月前
|
Linux 网络架构 Windows
TTL传输中过期原因,ttl传输中过期的解决办法(ttl传输中过期怎么解决)
A3: 实际上,TTL值需依据实际网络环境设定。过小的TTL值可能导致数据包过早丢弃,影响通信;反之,过大的TTL值则可能占用不必要的网络资源。因此,科学合理的TTL值设定是平衡通信效率与资源利用的关键。
1794 0
|
10月前
|
存储 负载均衡 NoSQL
一文让你搞懂 zookeeper
一文让你搞懂 zookeeper
10148 12
|
Java 数据库连接 API
Spring事务管理嵌套事务详解 : 同一个类中,一个方法调用另外一个有事务的方法
Spring事务管理嵌套事务详解 : 同一个类中,一个方法调用另外一个有事务的方法
893 1