java:synchronized关键字和lock的区别?

314 阅读1分钟

前提

1.Lock 和 synchronzied 的可见性是怎么实现的?

lock: 利用volatile相关的Happens-before规则,java 的AbstractQueuedSynchronizer 内部持有一个volatile修饰的state 变量:

  /**
     * The synchronization state.
     * 加锁会+1,解锁会-1;
     */
    private volatile int state;

根据volatile 可见性规则, 多个线程在读取state 的值时,永远得到的都是最新的,也就是,当前线程,对变量的加锁和解锁,对下一个线程,都是可见的;

synchronzied synchronized 的解锁 Happens-Before 于后续对这个锁的加锁。 当然,也可以从字节码角度来看实现原理:

public class Demo_01 {
    private final Object lock = new Object();

    public void test() {
        synchronized (lock) {
            // do sth....
        }
    }
}

执行:javap -verbose Demo_01 截取部分字节码:

 Code:
      stack=2, locals=3, args_size=1
         0: aload_0
         1: getfield      #3                  // Field lock:Ljava/lang/Object;
         4: dup
         5: astore_1
         6: monitorenter
         7: aload_1
         8: monitorexit
         9: goto          17
        12: astore_2
        13: aload_1
        14: monitorexit
        15: aload_2
        16: athrow
        17: return

留意 第6行和第14行的 monitorentermonitorexit,在这两个指令之间执行的代码,具备:顺序性,可见性,原子性;

一言概之:都是并发编程中 管程的实现;

下图,从程序员角度列出二者之间的差异:

ReentrantLock 和 Synchronized的区别.png

Tip:

  • 面试的时候,会问道: 关键字 synchronized 修饰实例方法、静态方法和代码块时,分别是什么含义?

  • 答:
  1. 修饰实例方法时,给当前对象实例加锁;

  2. 修饰静态方法时,给当前类加锁;

  3. 修饰代码块时,给指定对象加锁。

  4. 进入同步代码/代码块之前需要获得当前对象/当前类/指定对象的锁。