synchronized原理浅析

其实volatile和synchronized 两个操作在多线程中应用都很多,上篇文章谈到了volatile 这里主要说下后者


volatile和synchronized区别:

  • volatile用的恰当的话会比synchronized使用和执行成本更低,因为他不会引起线程上下文的切换和调度
  • 关键之synchronized可以修饰方法或者以同步块的形式来进行使用,他主要是确保多线程在同一时刻,只能有一个线程处于方法或者同步块中,他保证了线程对变量访问的可见性
  • 相对volatile ,synchronized是重量级锁

用synchronized锁的几种表现形式:

  • 对于普通同步方法,锁是当前实例对象
  • 对于静态同步方法,锁是当前类的Class对象
  • 对于同步方法快,锁是Synchronized括号里配置的对象

synchronized的锁是咋回事:

  1. 当一个对象试图访问同步代码块时,他首先必须得到锁,退出或者抛出异常时必须释放锁
  2. JVM基于进入(monitorenter)和退出(monitorexit) Monitor 来实现方法和代码块同步
  3. monitorenter指令是在编译后插入到同步代码块的开始位置,而monitorexit是插入到方法结束处和异常处,JVM要保证每个monitorenter必须有对应的monitorexit相匹配。任何对象都有一个monitor与之关联,并且当一个monitor被持有后,他处于锁定状态。线程执行到monitorenter指令时,将会尝试获取对象对应的monitor的所有权,即尝试获得对象的锁

对于以上原理的代码示例:

源码:

/**
 * 
 */
package com.hhx.offline_tools.encode;

/**
 * Synchronized获取锁的原理解析
 * @author 清水贤人
 *
 */
public class SynchronizedTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		
		//对Synchronized Class 对象进行加锁
		synchronized (SynchronizedTest.class) {
			
		}
		//静态同步方法,对Synchronized Class 对象进行加锁
		m();
	}
	
	public static synchronized void m(){
		
		
	}

}

编译后的代码:

{
  public com.hhx.offline_tools.encode.SynchronizedTest();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #8                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 11: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/hhx/offline_tools/encode/SynchronizedTest;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: ldc           #1                  // class com/hhx/offline_tools/encode/SynchronizedTest
         2: dup
         3: monitorenter                      //monitorenter :监视器进入,获取锁
         4: monitorexit			      //monitorexit :监视器退出,释放锁
         5: invokestatic  #16                 // Method m:()V
         8: return
      LineNumberTable:
        line 19: 0
        line 23: 5
        line 24: 8
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       9     0  args   [Ljava/lang/String;

  public static synchronized void m();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED  //同步方法依靠方法上的修饰符:ACC_SYNCHRONIZED来完成
    Code:
      stack=0, locals=0, args_size=0
         0: return
      LineNumberTable:
        line 29: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
}
SourceFile: "SynchronizedTest.java"

上面的class 信息中,对于同步块使用了monitorenter 和 monitorexit 指令,二同步方法则是依靠方法修饰符上的ACC_SYNCHRONIZED 来完成,无论猜中那种方式,其本质都是一个对象的监视器monitor进行获取,而这个过程是排他的。

任意对象都有自己的监视器,当这个对象由同步块或者这个对象的同步方法调用时,调用的线程必须先获取到该对象的监视器(monitor)才能进入,而没有获取的监视器的线程将会被阻塞,进入BLOCKED状态。这也解释了 文章开头提到的为啥synchronized关键字能保证多线程下的原子性。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值