搜索
写经验 领红包
 > 生活

java两年开发经验(java开发可以干几年)

导语:从事多年java开发,请说说synchronized的底层原理,内心有点慌

java两年开发经验(java开发可以干几年)

synchronized是由一对monitorenter和monitorexit指令来实现同步的,在jdk1.6之前,monitor的实现是依靠操作系统内部的互斥锁来实现的,所以需要进行用户态和内核态的切换,所以此时的同步操作是一个重量级的操作,性能很低。但是,jdk1.6带来了新的变化,提供了三种monitor的实现方式,分别是偏向锁,轻量级锁和重量级锁,即锁会先从偏向锁再根据情况逐步升级到轻量级锁和重量级锁-—这就是锁升级

jdk1.6之后JVM对synchronized进行了优化,经历三个的过程:

第一:在锁对象的对象头里面有一个threadid字段,默认情况下为空。当第一次有线程访问时,则将该threadid设置为当前的线程id,我们称为让其获取偏向锁。当线程执行结束,则重新将threadid设置为空。

第二:如果线程再次进入的时候,会先判断threadid与该线程的id是否一致。如果一致,则可以获取该对象。如果不一致,则发生锁升级,从偏向锁升级为轻量级锁。

第三:轻量级锁的工作模式是通过自旋循环的方式来获取锁,看对方线程是否已经释放了锁。如果执行一定次数之后,还是没有获取到锁,则发生锁升级,从轻量级锁升级为重量级锁。

锁升级初步

总结:使用锁升级的目的是为了减少锁带来的性能消耗。

通过反编译查看字节码,就可以看到相关的指令。源码:就是写了synchronized同步代码块控制线程安全。

// class version 52.0 (52)// access flags 0x21public class SynchronizedTest {  // compiled from: SynchronizedTest.java  // access flags 0x1  public <init>()V   L0    LINENUMBER 8 L0    ALOAD 0    INVOKESPECIAL java/lang/Object.<init> ()V    RETURN   L1    LOCALVARIABLE this LSynchronizedTest; L0 L1 0    MAXSTACK = 1    MAXLOCALS = 1  // access flags 0x9  public static main([Ljava/lang/String;)V    TRYCATCHBLOCK L0 L1 L2 null    TRYCATCHBLOCK L2 L3 L2 null   L4    LINENUMBER 10 L4    NEW java/lang/Object    DUP    INVOKESPECIAL java/lang/Object.<init> ()V    ASTORE 1   L5    LINENUMBER 11 L5    ALOAD 1    DUP    ASTORE 2    MONITORENTER   L0    LINENUMBER 13 L0    ALOAD 2    MONITOREXIT   L1    GOTO L6   L2   FRAME FULL [[Ljava/lang/String; java/lang/Object java/lang/Object] [java/lang/Throwable]    ASTORE 3    ALOAD 2    MONITOREXIT   L3    ALOAD 3    ATHROW   L6    LINENUMBER 14 L6   FRAME CHOP 1    RETURN   L7    LOCALVARIABLE args [Ljava/lang/String; L4 L7 0    LOCALVARIABLE obj Ljava/lang/Object; L5 L7 1    MAXSTACK = 2    MAXLOCALS = 4}

synchronized如何保证可见性的?

首先,我们需要知道可见性原理:两个线程如何保证变量信息的共享可见性?需要经历以下的流程

线程A -> 本地内存A(共享变量副本) -> 主内存(共享变量)

如果有变更,需要将本地内存的变量写到主内存,对方才可以获取到更新。这个是前提知识。那么,synchronized是如何保证可见性的呢?

结论:当获取到锁之后,每次读取都是从主内存读取,当释放锁的时候,都会将本地内存的信息写到主内存,从而实现可见性。

本文内容由小德整理编辑!