java 并发编程之关键字synchronized
in java with 0 comment

java 并发编程之关键字synchronized

in java with 0 comment

synchronized是java 并发中我们最为熟悉的关键字。

synchronized的使用方法:

1 普通同步方法

2 静态同步方法

3 同代码块

public class SynchronizedTest {
    public synchronized void test1(){

    }

    public void test2(){
        synchronized (this){

        }
    }
}

javap -v SynchronizedTest.class得到如下的Class的信息:

$ javap -v SynchronizedTest.class
Classfile /D:/code/opensource/jdk-source/jdk1.8/src/test/java/online/limingming/                                                                                                                                                                                               jdk/jdk1/SynchronizedTest.class
  Last modified 2018-9-9; size 439 bytes
  MD5 checksum d8236cee2b6f69c63b55606769afe729
  Compiled from "SynchronizedTest.java"
public class online.limingming.jdk.jdk1.SynchronizedTest
  SourceFile: "SynchronizedTest.java"
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #3.#16         //  java/lang/Object."<init>":()V
   #2 = Class              #17            //  online/limingming/jdk/jdk1/Synchro                                                                                                                                                                                               nizedTest
   #3 = Class              #18            //  java/lang/Object
   #4 = Utf8               <init>
   #5 = Utf8               ()V
   #6 = Utf8               Code
   #7 = Utf8               LineNumberTable
   #8 = Utf8               test1
   #9 = Utf8               test2
  #10 = Utf8               StackMapTable
  #11 = Class              #17            //  online/limingming/jdk/jdk1/Synchro                                                                                                                                                                                               nizedTest
  #12 = Class              #18            //  java/lang/Object
  #13 = Class              #19            //  java/lang/Throwable
  #14 = Utf8               SourceFile
  #15 = Utf8               SynchronizedTest.java
  #16 = NameAndType        #4:#5          //  "<init>":()V
  #17 = Utf8               online/limingming/jdk/jdk1/SynchronizedTest
  #18 = Utf8               java/lang/Object
  #19 = Utf8               java/lang/Throwable
{
  public online.limingming.jdk.jdk1.SynchronizedTest();
    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 13: 0

  public synchronized void test1();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_SYNCHRONIZED
    Code:
      stack=0, locals=1, args_size=1
         0: return
      LineNumberTable:
        line 16: 0

  public void test2();
    descriptor: ()V
    **flags: ACC_PUBLIC**
    Code:
      stack=2, locals=3, args_size=1
         0: aload_0
         1: dup
         2: astore_1
         **3: monitorenter**
         **4: aload_1
         5: monitorexit**
         6: goto          14
         9: astore_2
        10: aload_1
        11: monitorexit
        12: aload_2
        13: athrow
        14: return
      Exception table:
         from    to  target type
             4     6     9   any
             9    12     9   any
      LineNumberTable:
        line 19: 0
        line 21: 4
        line 22: 14
      StackMapTable: number_of_entries = 2
           frame_type = 255 /* full_frame */
          offset_delta = 9
          locals = [ class online/limingming/jdk/jdk1/SynchronizedTest, class ja                                                                                                                                                                                               va/lang/Object ]
          stack = [ class java/lang/Throwable ]
           frame_type = 250 /* chop */
          offset_delta = 4

}

可以看到同步代码块的指令中多处了monitorenter 和monitorexit。

同步方法中出现了flag:ACC_SYNCHRONIZED。

同步代码块:任何对象都有一个monitor与之相关联,当且一个monitor被持有之后,他将处于锁定状态。线程执行到monitorenter指令时,将会尝试获取对象所对应的monitor所有权,即尝试获取对象的锁;

同步方法:在VM字节码层面并没有任何特别的指令来实现被synchronized修饰的方法,而是在Class文件的方法表中将该方法的access_flags字段中的synchronized标志位置1,表示该方法是同步方法并使用调用该方法的对象或该方法所属的Class在JVM的内部对象表示Klass做为锁对象。

java对象头:

java 的对象头包含两部分:Mark Word(标记字段)、Klass Pointer. Klass Pointer是里存放的是指向class对象的指针,表明这是哪个class的对象。Mark Word存放是是对象运行时的数据:

主要包含以下几点:hashcode,对象分代年龄等。

锁一共有4种状态,级别从低到高依次是:无锁状态、偏向锁状态、轻量级锁状态和重量级锁状态,这几个状态会随着竞争情况逐渐升级。锁可以升级但不能降级,意味着偏向锁升级成轻量级锁后不能降级成偏向锁。这种锁升级却不能降级的策略,目的是为了提高获得锁和释放锁的效率。对象的MarkWord变化为下图:

java中锁的相关内容还有很多,今后可以继续介绍。

monitor对象:

什么是Monitor?我们可以把它理解为一个同步工具,也可以描述为一种同步机制,它通常被描述为一个对象。

与一切皆对象一样,所有的Java对象是天生的Monitor,每一个Java对象都有成为Monitor的潜质,因为在Java的设计中 ,每一个Java对象自打娘胎里出来就带了一把看不见的锁,它叫做内部锁或者Monitor锁。

Monitor 是线程私有的数据结构,每一个线程都有一个可用monitor record列表,同时还有一个全局的可用列表。每一个被锁住的对象都会和一个monitor关联(对象头的MarkWord中的LockWord指向monitor的起始地址),同时monitor中有一个Owner字段存放拥有该锁的线程的唯一标识,表示该锁被这个线程占用。其结构如下:

Owner:初始时为NULL表示当前没有任何线程拥有该monitor record,当线程成功拥有该锁后保存线程唯一标识,当锁被释放时又设置为NULL;

EntryQ:关联一个系统互斥锁(semaphore),阻塞所有试图锁住monitor record失败的线程。

RcThis:表示blocked或waiting在该monitor record上的所有线程的个数。

Nest:用来实现重入锁的计数。

HashCode:保存从对象头拷贝过来的HashCode值(可能还包含GC age)。

Candidate:用来避免不必要的阻塞或等待线程唤醒,因为每一次只有一个线程能够成功拥有锁,如果每次前一个释放锁的线程唤醒所有正在阻塞或等待的线程,会引起不必要的上下文切换(从阻塞到就绪然后因为竞争锁失败又被阻塞)从而导致性能严重下降。Candidate只有两种可能的值0表示没有需要唤醒的线程1表示要唤醒一个继任线程来竞争锁。

Monitor与java对象以及线程是如何关联的?

1.如果一个java对象被某个线程锁住,则该java对象的Mark Word字段中LockWord指向monitor的起始地址

2.Monitor的Owner字段会存放拥有相关联对象锁的线程id

Monitor具体是怎么实现的?

1.Monitor是在jvm底层实现的,底层代码是c++

2.Monitor的enter方法:获取锁

3.Monitor的exit方法:释放锁

4.Monitor的wait方法:为java的Object的wait方法提供支持

5.Monitor的notify方法:为java的Object的notify方法提供支持

6.Monitor的notifyAll方法:为java的Object的notifyAll方法提供支持

下一篇来介绍锁的优化以及的CAS的相关内容。

Responses