介绍

  • 在多线程操作中volatile关键字可以保证共享变量的内存可见性, 但是并不能保证操作的原子性, 这时候就需要用到锁, synchronized同步锁是java关键字, 是内置的语言实现.
  • synchronized加锁和线程结束或异常锁的释放过程由JVM进行控制
  • synchronized关键字可以使用在方法和同步代码块中, 不同的使用方式, 锁的结果是不同的
  • 重量级锁 + 可重入

synchronized底层原理

1.代码示例

package com.liuzhihang.tool.java;

/**
 * @author liuzhihang
 * @date 2018/06/11 16:05
 */
public class SynchronizedTest {

	private int i;

	private int j;

    public void syncTest1() {

        synchronized (this) {
        	i++;
        }
    }

    public synchronized void syncTest2() {

        	j++;
    }


}

2.使用 javap -v SynchronizedTest.class 查看代码的对应字节码如下:

$ javap -v SynchronizedTest.class
Classfile /C:/Users/liuzhihang/Desktop/SynchronizedTest.class
  Last modified 2018-7-10; size 518 bytes
  MD5 checksum ba48def77b226e7b9ac28121ec423c16
  Compiled from "SynchronizedTest.java"
public class com.liuzhihang.tool.java.SynchronizedTest
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
	// 常量池省略
{
  	// 构造方法省略

  public void syncTest1();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=3, locals=3, args_size=1
         0: aload_0
         1: dup
         2: astore_1
         3: monitorenter
         4: aload_0
         5: dup
         6: getfield      #2                    // Field i:I
         9: iconst_1
        10: iadd
        11: putfield      #2                    // Field i:I
        14: aload_1
        15: monitorexit
        16: goto          24
        19: astore_2
        20: aload_1
        21: monitorexit
        22: aload_2
        23: athrow
        24: return
      Exception table:
  		// 省略代码

  public synchronized void syncTest2();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_SYNCHRONIZED
    Code:
      stack=3, locals=1, args_size=1
         0: aload_0
         1: dup
         2: getfield      #3                  // Field j:I
         5: iconst_1
         6: iadd
         7: putfield      #3                  // Field j:I
        10: return
      LineNumberTable:
        line 22: 0
        line 23: 10
}
SourceFile: "SynchronizedTest.java"

3.结论

  • 同步语句块的实现使用的是 monitorenter 和 monitorexit 指令, 其中有两个 monitorexit 因为不能确保是正常结束还是异常结束, 所以另一个是用来确保异常结束时释放 monitor指令.
  • 同步方法时使用的是 flags中的 ACC_SYNCHRONIZED 来标识该方法为同步方法, JVM在调用该方法时便会执行相应的同步调用.
  • 每个线程都维护自己的监视器(monitor), 只要是同步调用进行相关操作时要先获得 monitor, 否则将被阻塞