Java:sychronize和lock

1.锁类型名称解析

  • 可重入锁:在执行对象的同步方法中不用再次获取锁
  • 可中断锁:在等待获取锁的过程中可以中断
  • 公平锁:以每个线程在获取锁的等待时间为凭证,等待时间长的在获取锁上具有优先权
  • 读写锁:读数据的时候多条线程不做同步,写的时候必须同步

2.Synchronized实现原理(同步代码块)

核心: Synchronized可以把任何一个非null对象作为"锁",在HotSpot JVM实现中,锁有个专门的名字:对象监视器(Object Monitor)
一个简单的加了Synchronized的Demo的编译过程:

public class SynchronizedDemo {
    public void method() {
        synchronized (this) {
            System.out.println("...");
        }
    }
}

Java:sychronize和lock_第1张图片

monitorenter:

每个对象都是一个监视器锁(monitor)。当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的所有权

  1. 如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor的所有者
  2. 如果线程已经占有该monitor,只是重新进入,则进入monitor的进入数加1
  3. 如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权
monitorexit:

执行monitorexit的线程必须是objectref所对应的monitor的所有者。指令执行时,monitor的进入数减1,如果减1后进入数为0,那线程退出monitor,不再是这个monitor的所有者。其他被这个monitor阻塞的线程可以尝试去获取这个 monitor 的所有权
1.monitorexit指令出现了两次,第1次为同步正常退出释放锁;第2次为发生异步退出释放锁

综上所述,Synchronized的语义底层是通过一个monitor的对象来完成,其实wait/notify等方法也依赖于monitor对象,这就是为什么只有在同步的块或者方法中才能调用wait/notify等方法,否则会抛出java.lang.IllegalMonitorStateException的异常的原因

3.Synchronized实现原理(同步方法)

一个简单的加了Synchronized的Demo的编译过程:

public class SynchronizedMethod {
    public synchronized void method() {
        System.out.println("Hello World!");
    }
}

Java:sychronize和lock_第2张图片

从编译的结果来看,方法的同步并没有通过指令 monitorenter 和 monitorexit 来完成(理论上其实也可以通过这两条指令来实现),不过相对于普通方法,其常量池中多了 ACC_SYNCHRONIZED 标示符。JVM就是根据该标示符来实现方法的同步的,具体过程:

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

4.Lock实现原理粗解(以ReentrantLock为例)

ReentrantLock把所有Lock接口的操作都委派到一个Sync类上,该类继承了AbstractQueuedSynchronizer
即,AbstractQueuedSynchronizer会把所有的请求线程构成一个CLH队列,当一个线程执行完毕(lock.unlock())时会激活自己的后继节点,但正在执行的线程并不在队列中,而那些等待执行的线程全部处于阻塞状态,经过调查线程的显式阻塞是通过调用LockSupport.park()完成,而LockSupport.park()则调用sun.misc.Unsafe.park()本地方法

5.区别:

synchronized Lock
存在层次 Java的关键字(普通方法,静态方法,代码块),在jvm层面上
锁的释放 ①同步代码执行完毕,释放锁,②执行异常,JVM释放锁 在finally释放,不然会死锁
锁的获取 一个线程获取锁,其他线程进入等待,不管获取锁的线程是否阻塞 获取锁的线程阻塞后,其他线程会试图获取锁
锁的状态 无法判断 可以判断
锁的类型 可重入,不可中断,非公平 可重入,可判断,可公平
锁的性能 少量同步 大量同步

你可能感兴趣的:(面试题汇总)