深挖锁(补充篇)

简介: 本文阅读大概需要8分钟。


一前记


针对公众号关注者针对Lock,synchronized和cmpxchg提出的疑问,这里特意补充一片来分析一下Lock和synchronized区别,以及cmpxchg的意义。


二Lock和synchronized的区别


我们在深挖锁上篇已经知道synchronized是由javac编译成moniterenter和moniterexit指令实现代码同步块的,也就是说synchronized的同步实现是由JVM对moniterenter与moniterexit指令的执行来实现的。而Lock类相关的锁实现是不依赖于JVM的虚拟机指令实现,而是直接通过java代码实现的。这里我们以重入锁ReetrantLock的lock方法的实现来说明:


static final class NonfairSync extends Sync {
    private static final long serialVersionUID = 7316153563782823691L;
    /**
     * Performs lock.  Try immediate barge, backing up to normal
     * acquire on failure.
     */
    final void lock() {
        if (compareAndSetState(0, 1))
            setExclusiveOwnerThread(Thread.currentThread());
        else
            acquire(1);
    }
    ...//省略
}


这里我们以ReentrantLock默认的非公平锁来进行分析,这里可以看到NonfairSync.lock()便是ReentrantLock.lock()的实现方法。ReentrantLock获取锁首先通过compareAndSetState()尝试将锁对象的state标志由期望值0设置为1,如果设置成功,则说明成功获取了锁,如果失败则通过调用同步队列的acquire()方法,将线程加入同步队列进行锁等待。这里compareAndSetState便是获取锁的关键实现,下面便是compareAndSetState的实现:


protected final boolean compareAndSetState(int expect, int update) {
    // See below for intrinsics setup to support this
    return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}


如果看过之前面“试题13解析-CAS与ABA”的同学应该对这个函数不陌生,正是CAS原子操作系列的函数,在处理器级别,CAS的原子性是由处理器对cmpxchg汇编指令加入LOCK_IF_MP前缀所提供的内存屏障进行保证的(深挖锁上篇),而synchronized对应的moniter虚拟机指令的也同样是由cmpxchg实现的。这里我们一定要理解原子性与锁是不同的概念,CAS为锁的加解锁操作提供了原子性操作的保证,但是CAS并不代表锁,CAS提供的是变量原子性操作的功能,例如Atomic包中的类的实现也是通过CAS来对变量进行原子性操作的。


其实,锁可以抽象成一个标记,线程尝试获取锁的过程,就是尝试将该标记设置为已上锁的过程,只要我们能保证只有一个线程可以成功设置该标记(即上锁过程的原子性),那么这个标记就是一把锁。Lock与synchronized(moniter指令)获取锁与释放锁过程中正是使用CAS来设置锁标志的。


Lock接口是JDK5之后提供的锁机制,其相对于关键字synchronized为程序员提供了更灵活的加锁解锁接口,以及公平锁非公平锁等更多的锁特性,本文意在解释原子性与锁的关系,为了避免陷入Lock实现的细节,因此这里就不展开说明Lock接口以及Lock实现依赖的同步队列AbstractQueuedSynchronizer的相关内容了。最后说一下区别synchronized是在JVM中实现的,而Lock的锁逻辑是Java层直接实现的。

相关文章
|
6月前
|
Java 数据库 开发者
|
3月前
|
Java 开发者
解锁Java并发编程的秘密武器!揭秘AQS,让你的代码从此告别‘锁’事烦恼,多线程同步不再是梦!
【8月更文挑战第25天】AbstractQueuedSynchronizer(AQS)是Java并发包中的核心组件,作为多种同步工具类(如ReentrantLock和CountDownLatch等)的基础。AQS通过维护一个表示同步状态的`state`变量和一个FIFO线程等待队列,提供了一种高效灵活的同步机制。它支持独占式和共享式两种资源访问模式。内部使用CLH锁队列管理等待线程,当线程尝试获取已持有的锁时,会被放入队列并阻塞,直至锁被释放。AQS的巧妙设计极大地丰富了Java并发编程的能力。
38 0
|
5月前
|
Java
JAVA多线程深度解析:线程的创建之路,你准备好了吗?
【6月更文挑战第19天】Java多线程编程提升效率,通过继承Thread或实现Runnable接口创建线程。Thread类直接继承启动简单,但限制多继承;Runnable接口实现更灵活,允许类继承其他类。示例代码展示了两种创建线程的方法。面对挑战,掌握多线程,让程序高效运行。
25 1
|
5月前
|
Java
探秘死锁:原理、发生条件及解决方案
探秘死锁:原理、发生条件及解决方案
98 1
|
6月前
|
算法 Java
Java多线程基础-13:一文阐明死锁的成因及解决方案
死锁是指多个线程相互等待对方释放资源而造成的一种僵局,导致程序无法正常结束。发生死锁需满足四个条件:互斥、请求与保持、不可抢占和循环等待。避免死锁的方法包括设定加锁顺序、使用银行家算法、设置超时机制、检测与恢复死锁以及减少共享资源。面试中可能会问及死锁的概念、避免策略以及实际经验。
96 1
|
6月前
|
Java 开发者
你知道偏向锁已经被废弃了吗?谈谈你对此的看法。
你知道偏向锁已经被废弃了吗?谈谈你对此的看法。
183 0
|
缓存 监控 算法
内容服务锁优化实践
内容服务锁优化实践
90 0
|
安全 算法 Java
【Java并发编程 十三】死锁问题及解决方案
【Java并发编程 十三】死锁问题及解决方案
97 0
|
存储 安全 Java
大白话讲解synchronized锁升级套路
synchronized锁是啥?锁其实就是一个对象,随便哪一个都可以,Java中所有的对象都是锁,换句话说,Java中所有对象都可以成为锁。 这次我们主要聊的是synchronized锁升级的套路
120 0
synchronized锁升级原理剖析 ✨ 每日积累
synchronized锁升级原理剖析 ✨ 每日积累
synchronized锁升级原理剖析 ✨ 每日积累