Synchronized底层原理

纯属记录个人理解,有误请指出

一、Synchronized加锁方式:

1)同步实例方法,锁是当前实例对象

public synchronized void method() {
    /*修饰实例方法,锁的是当前实例对象*/
}
synchronized(this) {
    /*修饰同步代码块,锁的是当前实例对象*/
}

2)同步类方法,锁是该类类对象

public static synchronized void method() {
    /*修饰静态方法,锁的是该类类对象*/
}
synchronized(Demo.class) {
    /*修饰同步代码块,锁的是该类类对象*/
}

3)同步代码块,锁是括号里面的对象

String object = "";
synchronized(object) {
    /*修饰同步代码块,锁的是实例对象Object*/
}
二、Synchronized特性:

1)互斥性:Synchronized修饰的同步代码块、实例方法、静态方法,多线程并发访问时,只能有一个线程获取到锁,为非公平锁,每个刚来的线程都会尝试下插队,如果插不了才进入等待队列,进入阻塞状态等待。

2)可见性:某线程 A 对于进入同步块之前或在 synchronized 中对于共享变量的操作,对于后续的持有同一个监视器锁的其他线程可见

同步方法和同步代码块底层都是通过monitor来实现同步的。每个对象都与一个monitor相关联。

同步方法是通过方法中的access_flags中设置ACC_SYNCHRONIZED标志来实现;同步代码块是通过monitorenter和monitorexit来实现。两个指令的执行是JVM通过调用操作系统的互斥原语mutex来实现,被阻塞的线程会被挂起、等待重新调度,会导致“用户态和内核态”两个态之间来回切换,对性能有较大影响。

三、查看ACC_SYNCHRONIZED标志

查看字节码在终端操作的命令为

javac SyncDemo.java

javap -c -verbose SyncDemo

public class SyncDemo {
    public static int sum = 0;
    public static synchronized void addSum() {
        sum++;
        System.out.println(sum);
    }

    public static void main(String[] args) {
        new Thread("a") {
            public void run() {
                addSum();
            }
        }.start();
        new Thread("b") {
            public void run() {
                addSum();
            }
        }.start();
    }
}

查看结果:我们可以看到在flags中有 ACC_SYNCHRONIZED 标志

 public static synchronized void addSum();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED 
    Code:
      stack=2, locals=0, args_size=0
         0: getstatic     #11                 // Field sum:I
         3: iconst_1
         4: iadd
         5: putstatic     #11                 // Field sum:I
         8: return
      LineNumberTable:
        line 20: 0
        line 21: 8

四、查看同步代码块的monitor
public class SyncDemo {
    public static int sum = 0;
    public static void addSum() {
        synchronized (SyncDemo.class) {
            sum++;
            System.out.println(sum);
        }
    }

    static SyncDemo syncDemo;
    public static void main(String[] args) {
        new Thread("a") {
            public void run() {
                addSum();
            }
        }.start();
        new Thread("b") {
            public void run() {
                addSum();
            }
        }.start();
    }
}

查看结果:我们可以看到第9行monitorenter加锁,第23行monitorexit释放锁,第29行monitorexit为发生异常释放锁

 public static void addSum();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=2, args_size=0
         0: ldc           #2                  // class pang/one/SyncDemo
         2: dup
         3: astore_0
         4: monitorenter
         5: getstatic     #3                  // Field sum:I
         8: iconst_1
         9: iadd
        10: putstatic     #3                  // Field sum:I
        13: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
        16: getstatic     #3                  // Field sum:I
        19: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
        22: aload_0
        23: monitorexit
        24: goto          32
        27: astore_1
        28: aload_0
        29: monitorexit
        30: aload_1
        31: athrow
        32: return
      Exception table:
         from    to  target type
             5    24    27   any
            27    30    27   any
      LineNumberTable:
        line 7: 0
        line 8: 5
        line 9: 13
        line 10: 22
        line 11: 32
      StackMapTable: number_of_entries = 2
        frame_type = 255 /* full_frame */
          offset_delta = 27
          locals = [ class java/lang/Object ]
          stack = [ class java/lang/Throwable ]
        frame_type = 250 /* chop */
          offset_delta = 4

五、查看monitor中的锁级别

锁级别有:

锁状态1bit(是否偏向锁)2bit(锁标志位)
无锁态001
轻量级锁000
重量级锁010
GC标记011
偏向锁101

没加锁

import org.openjdk.jol.info.ClassLayout;

public class SyncDemo {
    static SyncDemo syncDemo;
    
    public static void main(String[] args) {
        syncDemo = new SyncDemo();
        //synchronized (syncDemo) {
            //System.out.println("locking");
            syncDemo.hashCode();
            System.out.println(ClassLayout.parseInstance(syncDemo).toPrintable());
        //}
    }
}

输出结果:无锁态

在这里插入图片描述


加锁之后

import org.openjdk.jol.info.ClassLayout;

public class SyncDemo {
    static SyncDemo syncDemo;
    
    public static void main(String[] args) {
        syncDemo = new SyncDemo();
        synchronized (syncDemo) {
            System.out.println("locking");
            syncDemo.hashCode();
            System.out.println(ClassLayout.parseInstance(syncDemo).toPrintable());
        }
    }
}

输出结果:重量级锁

在这里插入图片描述

六、Synchronized锁升级过程

​ 1、在无锁状态,线程1访问同步块进行CAS修改MarkWord,进入偏向锁状态,因为偏向锁是在单线程的情况

​ 2、当进行另一个线程2访问同步块时,CAS修改MarkWord失败,开始撤销偏向锁,升级为轻量级锁

​ 3、而在线程2继续访问同步块进行CAS修改MarkWord时,自旋获取锁失败一定次数后,锁膨胀,升级为重量级锁,从而线程阻塞;

​ 4、直到等待线程1释放锁唤醒阻塞的线程,开始新的一轮锁竞争。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值