Synchronized底层实现

Synchronized底层实现

synchronized关键字锁的都是对象,不是代码。
synchronized可以修饰静态方法、成员函数,同时还可以直接定义代码块,但是它上锁的资源只有两类:一个是对象,一个是类。

  • 对象锁和类锁
    对象锁:只作用于同一个对象,如果调用两个同一类的对象上的同步代码块,就不会同步。
    类锁:作用于当前整个类,如果两个线程调用同一个类的不同对象上的同步语句,实惠同步的。

  • 等待池和锁池
    等待池:某一现场调用了某一对象的wait()方法之后,档期啊线程就会释放该对象的锁,进入到该对象的等待池中。
    锁池:获取某一对象的monitor lock,如果当前对象的monitor lock呗其他线程所获取了,当前没有获取到该对象monitor lock的线程就会进入当前对象的锁池中。

  • Synchronized关键字同步方法的底层原理
    在方法执行期间,执行线程持有了monitor,其他任何线程都无法再获得同一个monitor。如果一个同步方法执行期间抛 出了异常,并且在方法内部无法处理此异常,那这个同步方法所持有的monitor将在异常抛到同步方法之外时自动释放。

    public class Test1 {
    	public int i;
    	public synchronized void myMethod() {
    		i++;
    	}
    }

相应字节码

  {
      public int i;
        descriptor: I
        flags: ACC_PUBLIC
     
      public com.myOwn.demo.Test1();
        descriptor: ()V
        flags: ACC_PUBLIC
        Code:
          stack=1, locals=1, args_size=1
             0: aload_0
             1: invokespecial #1                  // Method java/lang/Object."<init>":()V
             4: return
          LineNumberTable:
            line 3: 0
     
      public synchronized void myMethod();
        descriptor: ()V
        flags: ACC_PUBLIC, ACC_SYNCHRONIZED
        Code:
          stack=3, locals=1, args_size=1
             0: aload_0
             1: dup
             2: getfield      #2                  // Field i:I
             5: iconst_1
             6: iadd
             7: putfield      #2                  // Field i:I
            10: return
          LineNumberTable:
            line 8: 0
            line 9: 10
    }

synchronized修饰的方法并没有monitorenter指令和monitorexit指令,而是有一个ACC_SYNCHRONIZED标识,该标识指明了该方法是一个同步方法,JVM通过该ACC_SYNCHRONIZED访问标志来辨别一个方法是否声明为同步方法,从而执行相应的同步调用。这就是synchronized锁在同步方法上实现的基本原理。

  • Synchronized关键字同步代码块的底层原理
public class Test2 {
 	public static void main(String[] args) {
		synchronized(MySync3.class) {
			System.out.println("MySync3");
		}
	}
}

相应字节码

public class com.myOwn.demo.Test2 {
  public com.myOwn.demo.Test2();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return
 
  public static void main(java.lang.String[]);
    Code:
       0: ldc           #2                  // class com/myOwn/demo/MySync3
       2: dup
       3: astore_1
       4: monitorenter        //注意此处有monitorenter指令
       5: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
       8: ldc           #4                  // String MySync3
      10: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      13: aload_1
      14: monitorexit        //注意此处有monitorexit指令
      15: goto          23
      18: astore_2
      19: aload_1
      20: monitorexit        //注意此处有monitorexit指令
      21: aload_2
      22: athrow
      23: return
    Exception table:
       from    to  target type
           5    15    18   any
          18    21    18   any
}

同步语句块的实现使用的是monitorenter 和 monitorexit 指令,其中monitorenter指令指向同步代码块的开始位置,monitorexit指令则指明同步代码块的结束位置,当执行monitorenter指令时,当前线程将试图获取 objectref(即对象锁) 所对应的 monitor 的持有权,当 objectref 的 monitor 的进入计数器为 0,那线程可以成功取得 monitor,并将计数器值设置为 1,取锁成功。如果当前线程已经拥有 objectref 的 monitor 的持有权,那它可以重入这个 monitor (关于重入性稍后会分析),重入时计数器的值也会加 1。倘若其他线程已经拥有 objectref 的 monitor 的所有权,那当前线程将被阻塞,直到正在执行线程执行完毕,即monitorexit指令被执行,执行线程将释放 monitor(锁)并设置计数器值为0 ,其他线程将有机会持有 monitor 。

  • Synchronized锁升级(偏向锁、轻量级锁、重量级锁)

1.锁的状态:
a.无锁状态
b.偏向锁状态
c.轻量级锁状态
d.重量级锁状态

注意:四种状态会随着竞争的情况逐渐升级,升级之后不可逆,即不可降级。

2.偏向锁
a.偏向锁的使用:测试对象头Mark Word里是否存储着指向当前线程的偏向锁。若测试失败,则测试Mark Word中偏向锁标识是否设置成1(表示当前为偏向锁),没有设置则使用CAS竞争,否则尝试使用CAS将对象头的偏向锁指向当前线程。
b.偏向锁的升级:当有第二个线程进入同步代码块时,则升级为轻量级锁。

3.轻量级锁
a.轻量级锁的加锁:如果成功使用CAS将对象头重的Mark Word替换为指向锁记录的指针,则获得锁,失败则当前线程尝试使用自旋(循环等待)来获取锁。
b.轻量级锁的解锁:当有另一个线程与该线程同时竞争时,锁会升级为重量级锁。为了防止继续自旋,一旦升级,将无法降级。

4.重量级锁
重量级锁的特点:其他线程试图获取锁时,都会被阻塞,只有持有锁的线程释放锁之后才会唤醒这些线程,进行竞争。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值