并发编程02——synchronized&Lock&AQS详解
发布日期:2021-05-16 22:57:23 浏览次数:22 分类:精选文章

本文共 1534 字,大约阅读时间需要 5 分钟。

一、并发同步器-设计同步器的意义

在多线程编程中,共享和可变的资源可能会导致线程安全问题。这些资源可以是对象、变量、文件等。共享意味着多个线程可以同时访问,而可变意味着资源在生命周期内可能会被修改。为了确保线程安全,需要采取同步机制来协调对临界资源的访问。

并发模式的解决方案

所有并发模式都采用的方案是序列化访问临界资源,即在同一时刻,只能有一个线程访问临界资源,这称为同步互斥访问。Java提供了两种方式来实现同步互斥访问:synchronized和Lock。

Java锁体系

synchronized是基于JVM内置锁,通过Monitor(监视器锁)实现,依赖底层操作系统的Mutex lock。synchronized关键字会被编译成monitorenter和monitorexit两条指令,分别对应JMM中的八大原子操作。

每个对象都有一个Monitor,加锁过程如下图所示。Monitor的状态记录在对象的内存布局中的Mark Word部分。Mark Word在32位和64位虚拟机中分别为32个和64个Bits,根据对象的状态复用存储空间。

synchronized的使用与原理

synchronized修饰的实例方法锁的是对象(this),同步代码块锁的是代码块内的对象。类锁锁的是类对象,类锁和对象锁是互不干扰的。

synchronized的加锁方式有三种:同步实例方法、同步代码块、同步类方法。加锁的目的是序列化访问临界资源,确保同步互斥访问。

锁状态与内存布局

每个对象的内存布局包括对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。对象头存储运行时数据,如哈希码、GC分代年龄、锁状态标志、线程持有的锁等。Mark Word是对象头的一部分,用于存储锁状态。

锁膨胀升级

JVM对synchronized进行了多项优化,如锁粗化、锁消除、轻量级锁、偏向锁和自旋锁。锁的状态从无锁到偏向锁再到轻量级锁和重量级锁。偏向锁优化了无锁状态下加锁的性能,轻量级锁减少了锁的重量,自旋锁减少了线程切换的开销。

逃逸分析

逃逸分析是JVM优化的一部分,用于减少不必要的锁开销。通过分析代码,确定哪些对象不会逃逸到堆区,可以对这些对象进行栈分配和标量替换优化。逃逸分析默认开启,可以通过JVM参数关闭。

Lock的实现

Lock是另一种同步机制,相比synchronized更接近操作系统的Mutex锁。Lock提供了更高的灵活性,可以实现更复杂的同步需求。Java的ReentrantLock是基于AQS实现的,支持可重入和公平/非公平锁模式。

AbstractQueuedSynchronizer(AQS)

AQS是Java并发编程的核心框架,定义了独占锁和共享锁两种资源共享方式。AQS通过同步等待队列(CLH队列)和条件等待队列实现线程等待和唤醒。

AQS的状态管理

AQS维护一个volatile int状态字段,通过getState、setState和compareAndSetState方法进行操作。独占锁和共享锁的实现依赖于这些方法。

公平锁与非公平锁

公平锁和非公平锁的主要区别在于加锁失败时的处理方式。公平锁优先让等待最久的线程获得锁,而非公平锁则直接抢占锁。非公平锁在加锁失败时不会入队,而是立即抢占锁。

读写锁

读写锁是一种更细粒度的锁机制。写锁是独享锁,读锁是共享锁。AQS通过将状态字段按位切割,分别管理读锁和写锁的数量。这种设计允许更灵活地控制资源的访问。


以上内容涵盖了Java中的synchronized、Lock以及AQS框架的核心知识,包括它们的实现原理、优化机制和实际应用场景。

上一篇:webservice使用Axis2框架时-wsdl2java命令及参数
下一篇:MySQL中使用group_concat遇到的坑

发表评论

最新留言

不错!
[***.144.177.141]2025年04月30日 22时44分27秒