美文网首页
synchronized原理

synchronized原理

作者: baifanger | 来源:发表于2020-11-06 20:25 被阅读0次

    1.作用
    synchronized是java提供的一种最基本的锁,可重入的非公平锁,主要用在多线程并发中,当要求某种操作在同一时间只能由一个线程处理时,就需要加锁,否则就会造成数据的错乱。
    2.锁类型
    synchronized可用在static代码块,静态方法,实例方法以及方法块中,不同的地方所用的锁是不一样的。


    3.实现的原理
    
    public class SynTest {
        private final Object lock = new Object();
        private int a = 1;
    
        public void test() {
            synchronized (lock) {
                a++;
            }
        }
    }
    

    以这段简单的代码为例,我们用任意实例对象Object来加的锁,编译成.class文件后,我们再看一下代码(比较多,只截取了重要的部分):

        TRYCATCHBLOCK L0 L1 L2 null
        TRYCATCHBLOCK L2 L3 L2 null
       L4
        LINENUMBER 8 L4
        ALOAD 0
        GETFIELD com/game/xiangxuemytest/thread/SynTest.lock : Ljava/lang/Object;
        DUP
        ASTORE 1
        MONITORENTER
       L0
        LINENUMBER 9 L0
        ALOAD 0
        DUP
        GETFIELD com/game/xiangxuemytest/thread/SynTest.a : I
        ICONST_1
        IADD
        PUTFIELD com/game/xiangxuemytest/thread/SynTest.a : I
       L5
        LINENUMBER 10 L5
        ALOAD 1
        MONITOREXIT
    

    在编译后的代码中可以看到 ** MONITORENTER,MONITOREXIT**,两个指令,这两个指令就是同步的关键。当线程执行代码前,需要先执行monitorEnter,哪个线程获取了monitor对象,便可执行代码,否则线程会进入阻塞状态。当代码运行完成后,会执行monitorExit,将对象释放。
    注意:synchronized是一种可重入锁。

    4.锁的状态
    锁在哪里:在同步的时候是获取对象的monitor,即获取到对象的锁。那么对象的锁怎么理解?无非就是类似对对象的一个标志,那么这个标志就是存放在Java对象的对象头。Java对象头里的Mark Word里默认的存放的对象的Hashcode,分代年龄和锁标记位。32为JVM Mark Word默认存储结构为(注:摘自《java并发编程的艺术》):

    锁的状态:在jdk1.5之前,synchronized在实现上是一种很重的锁,会导致线程的阻塞;在1.5及之后,将其做了优化,将锁的状态分成了4类:无锁、偏向锁、轻量级锁、重量级锁,这几个状态会随着锁的竞争,逐步升级,锁可以升级但不能降级,也就意味着一旦升级后,即可不存在竞争了,那锁也无法降级了。对象的MarkWord变化为下图


    偏向锁:HotSpot的作者经过研究发现,大多数情况下,锁不仅不存在多线程竞争,而且总是由同一线程多次获得,为了让线程获得锁的代价更低而引入了偏向锁。
        当一个线程访问同步块并获取锁时,会在对象头和栈帧中的锁记录里存储锁偏向的线程ID,当此线程来获取对象锁时,只需简单地测试一下对象头的Mark Word里是否存储着指向当前线程的偏向锁,如果通过了,此线程就会执行代码,如果没通过,则锁存在着线程竞争,则升级为轻量级锁。
    轻量级锁:线程在执行同步块之前,JVM会先在当前线程的栈桢中创建用于存储锁记录的空间,并将对象头中的Mark Word复制到锁记录中,官方称为Displaced Mark Word。当有线程来获取锁时,会尝试使用CAS将对象头中的Mark Word替换为指向锁记录的指针。如果成功,当前线程获得锁,如果失败,表示其他线程竞争锁,当前线程便尝试使用自旋来获取锁。当自旋达到一定次数时依然未得到锁时,说明线程竞争比较多,该轻量级锁就会升级为重量级锁。
    重量级锁:也就是jdk1.5之前的实现方式了,当有线程竞争锁失败时,会进入阻塞状态,而这中间上下文的切换,会加大cpu的开销。

    5.锁的比较

    锁的比较

    参考:https://www.jianshu.com/p/d53bf830fa09

    相关文章

      网友评论

          本文标题:synchronized原理

          本文链接:https://www.haomeiwen.com/subject/tmfxbktx.html