Java中的各种锁(2)——隐式锁synchronized

2 Java中的隐式锁

在Java中,提供了关键字synchronized。这个关键字可以应用在不同的地方,如下面的表格所示:

应用位置 锁存在于哪里 代码示例
实例方法 当前类的实例对象 public synchronized void method(){......}
静态方法 当前类对象 public static synchronized void method(){......}
代码块 指定的实例对象 synchronized(object){......} //这里的object可以是任何的对象实例,比如this,Integer.MAX_VALUE这样的对象实例,都可以
代码块 指定的类对象 syncrhonized(Demo.class){......}

使用了synchronized关键字之后,就可以给相应的方法/代码块加上锁,保证在一个JVM中,同时只有一个线程能够执行这个方法/这个代码。但是我们并没有看到加锁和释放锁的操作,因此又被称为“隐式锁”。

synchronized的这个功能是在JVM中通过使用monitor来实现的。

2.1 sychronized对应的字节码

我们先来看看下面的代码:

public class DemoOnInstance {
    public int value = 0;

  public int addAndGet(int increment){
      synchronized (this){
          value = value + increment;
          return value;
      }
  }
}

在上面的代码中的addAndGet方法里面,我们使用了synchronized关键字标记了一个同步代码块,并且是将锁加在了当前实例this上。那么,JVM是怎么帮我们实现锁的呢。我们可以看看生成的字节码。
我们可以在编译后的class文件上使用javap命令来查看字节码,javap -v DemoOnInstance.class。得到的结果会包含下面的片段,这个片段是addAndGet方法对应的字节码。

  public int addAndGet(int);
    descriptor: (I)I
    flags: ACC_PUBLIC
    Code:
      stack=3, locals=4, args_size=2
         0: aload_0
         1: dup
         2: astore_2
         3: monitorenter
         4: aload_0
         5: aload_0
         6: getfield      #2                  // Field value:I
         9: iload_1
        10: iadd
        11: putfield      #2                  // Field value:I
        14: aload_0
        15: getfield      #2                  // Field value:I
        18: aload_2
        19: monitorexit
        20: ireturn
        21: astore_3
        22: aload_2
        23: monitorexit
        24: aload_3
        25: athrow
      Exception table:
         from    to  target type
             4    20    21   any
            21    24    21   any

上面的字节码中,请注意标记行号为3和19的两行,分别为monitorentermonitorexit。这两行指令告诉JVM要获取Monitor和释放Monitor。 我们可以看到第23行也有monitorexit,这个是异常情况下的释放monitor的处理。因此无论代码是正常执行还是异常执行,都会执行monitorexit,保证锁会被释放。

synchronized关键字应用在方法上的时候,情况略有不同。

public class DemoOnInstanceMethod {
  private int value = 0;

  public synchronized int addAndGet(int increment){
    value = value + increment;
    return value;
  }
}

上面的代码中的addAndGet方法编译后的字节码如下:

  public synchronized int addAndGet(int);
    descriptor: (I)I
    flags: ACC_PUBLIC, ACC_SYNCHRONIZED
    Code:
      stack=3, locals=2, args_size=2
         0: aload_0
         1: aload_0
         2: getfield      #2                  // Field value:I
         5: iload_1
         6: iadd
         7: putfield      #2                  // Field value:I
        10: aload_0
        11: getfield      #2                  // Field value:I
        14: ireturn

在字节码中,没有看到monitor相关的指令,但是在方法的flags中,有ACC_SYNCHRONIZED这个值。JVM在执行该方法的时候,如果有这个flag,就会获取锁,方法退出时(无论是正常退出还是异常退出)释放锁。

2.2 有了字节码之后呢?

synchronize关键字在不同的位置会生成不同的字节码,JVM在执行时会添加获取monitor和释放monitor的操作。 前面我们提到了,不同的代码锁定的对象是不一样的。那么这个是怎么实现的呢?

前面也提到了,不同的代码方式,锁存在的位置是不一样的,这是因为在Java中,对应 synchronized 有两种锁:对象锁和类锁。
对象锁:在非静态方法上使用 synchronized 关键字,或者使用 synchronized(objectInstance)这样的方式来创建同步代码块,使用的是对象锁。每个对象实例都有一个对象锁,不同的对象实例各自有各自的对象锁。对象锁是线程可重入的,因此在同一个线程中,可以在一个同步方法中调用另外一个同步方法。但是,一个线程在执行一个实例上的同步代码,其他线程如果要执行同样实例上的同步代码,无论是不是相同的代码段,都会被阻塞。如果两个线程执行的是同一个类的不同实例对象的同步代码块,则可以同时执行。

类锁:在静态方法上使用 synchronized 关键字,或者使用 synchronized(Demo.class)这样的方法创建的同步代码块,使用的就是类锁。一个类只有一个类锁(感觉类似静态变量)。同样的,使用同一个类锁的同步代码段,同一时间只有一个能执行。

类锁和对象锁互不干扰。

2.3 锁的升级

新创建的类锁或者对象锁,都是处于偏向锁的状态。在使用过程中,随着竞争的情况,会逐步升级为轻量锁状态或者重量锁状态。这一点在上一篇文章中已经讲过了,就不再赘述。

2.4 小结

synchronized 关键字会使用“隐式锁”,这个是由字节码和JVM共同来实现的,因此这个锁是“本地锁”,只能应用于同一个JVM中的不同进程,不能应用于不同的JVM之间。

不同的 synchronized 的用法使用的锁不尽相同,有对象锁和类锁两种。对象锁和类锁都是可重入锁和独享锁,同时也都是非公平锁。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,013评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,205评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,370评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,168评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,153评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,954评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,271评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,916评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,382评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,877评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,989评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,624评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,209评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,199评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,418评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,401评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,700评论 2 345

推荐阅读更多精彩内容