转载

JAVA锁介绍

JAVA锁介绍

之前的文章中介绍了 JAVA 中一些并发锁使用方法以及里面的介绍。同时之后还介绍了字节码的操作码,让大家先了解下里面的指令,我这里也是从表面中去讲解下锁底层操作码的实现。

锁对象程序:

package com.montos.detail;
public class SynchronizedDemo {
	public static void main(String[] args) {
		SynchronizedDemo demo = new SynchronizedDemo();
		demo.demo();
	}
	public void demo() {
		synchronized (this) {
			System.out.println("this is demo");
		}
	}
}
复制代码

对其反编译:

public class com.montos.detail.SynchronizedDemo {
  public com.montos.detail.SynchronizedDemo();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #2                  // class com/montos/detail/SynchronizedDemo
       3: dup
       4: invokespecial #3                  // Method "<init>":()V
       7: astore_1
       8: aload_1
       9: invokevirtual #4                  // Method demo:()V
      12: return
  public void demo();
    Code:
       0: aload_0
       1: dup
       2: astore_1
       3: monitorenter
       4: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
       7: ldc           #6                  // String this is demo
       9: invokevirtual #7                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      12: aload_1
      13: monitorexit
      14: goto          22
      17: astore_2
      18: aload_1
      19: monitorexit
      20: aload_2
      21: athrow
      22: return
    Exception table:
       from    to  target type
           4    14    17   any
          17    20    17   any
}

复制代码

查看上面反编译的结果,我们可以看到反编译里面是存在 monitorenter 以及 monitorexit 的操作码,这两个操作码的作用就是:

  1. monitorenter :每个对象都是一个监视器锁(monitor)。当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的所有权,过程如下:
  • 如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor的所有者;
  • 如果线程已经占有该monitor,只是重新进入,则进入monitor的进入数加1;
  • 如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权;
  1. monitorexit :执行monitorexit的线程必须是objectref所对应的monitor的所有者。指令执行时,monitor的进入数减1,如果减1后进入数为0,那线程退出monitor,不再是这个monitor的所有者。其他被这个monitor阻塞的线程可以尝试去获取这个 monitor 的所有权。

从而达到线程之间的串行执行,同时我可以看到里面有两次 monitorexit 操作码:第1次为同步正常退出释放锁;第2次为发生异步退出释放锁;这上面锁住的就是this。

锁方法程序:

public class SynchronizedDemo {
	public synchronized void method() {
		System.out.println("this is demo");
	}
}
复制代码

反编译:

public com.montos.detail.SynchronizedDemo();
    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 method();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_SYNCHRONIZED
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #3                  // String this is demo
         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 5: 0
        line 6: 8
复制代码

通过上面反编译,我们发现没有之前的两个操作码了,多出来的是有标识 ACC_SYNCHRONIZED ,这里其实也是通过上面两个操作码完成的。这个方法也只是比普通的方法在常量池中多了 ACC_SYNCHRONIZED 字段。

当方法调用时,调用指令将会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,执行线程将先获取monitor,获取成功之后才能执行方法体,方法执行完后再释放monitor。在方法执行期间,其他任何线程都无法再获得同一个monitor对象。

上面的两种操作本质上没有区别,只是方法的同步是一种隐式方式操作的,两个指令的执行是JVM通过调用操作系统的互斥原语mutex来实现,被阻塞的线程会被挂起、等待重新调度,会导致“用户态和内核态”两个态之间来回切换,对性能有较大影响。

锁定关键点:

对象在内存中布局主要有:对象头,实例数据以及对齐填充。

JAVA锁介绍
Java

Synchronized 用的锁就是存在 Java 对象头里的,那么什么是 Java 对象头呢?Hotspot虚拟机的对象头主要包括两部分数据: Mark Word (标记字段)、 Class Pointer (类型指针)。其中 Class Pointer 是对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例, Mark Word 用于存储对象自身的运行时数据,它是实现轻量级锁和偏向锁的关键。

这里面我们主要注意的是 Mark Word 这个存储结构。

JAVA锁介绍

每一个 Java 对象创建出来就带了一把看不见的锁,它叫做内部锁或者 Monitor锁Monitor 对象存在于每个 Java 对象的对象头 Mark Word 中(存储的指针的指向), Synchronized 锁便是通过这种方式获取锁的,也是为什么 Java 中任意对象可以作为锁的原因,同时 notify/notifyAll/wait 等方法会使用到 Monitor 锁对象,所以必须在同步代码块中使用。

原文  https://juejin.im/post/5d09d1676fb9a07eef6a10a3
正文到此结束
Loading...