● Synchronization的底层实现概述
另外的线程想获取对象头中的锁信息的时候,会发现对象头中已经记录一把锁(monitor),他就获取不到。monitor是互斥的,对象头记录的monitor就不会分配给其他线程了,此时这个线程就会进入阻塞状态。当执行中的线程发生异常,或者是释放锁标记,对象头的锁信息就会释放它记录的monitor。阻塞状态的线程就会弹出来一同争夺,重新在锁信息中记录monitor。
ObjectMonitor(java的monitor的实现)中有两个队列,_WaitSet 和 _EntryList,以及_Owner标记。其中WaitSet是用于管理等待队列(wait)线程的,EntryList 是用于管理锁池阻塞线程的,_Owner 标记用于记录当前执行线程。
当多线程并发访问同一个同步代码时,首先会进入EntryList,当线程获取锁标记后,monitor 中的Owner 记录此线程,并在 monitor 中的计数器执行递增计算(+1),代表锁定,其他线程在EntryList 中继续阻塞。若执行线程调用 wait 方法,则 monitor 中的计数器执行赋值为 0 计算,并将Owner 标记赋值为 null,代表放弃锁,执行线程进入WaitSet 中阻塞。若执行线程调用 notify/notifyAll 方法,WaitSet 中的线程被唤醒,进入EntryList 中阻塞,等待获取锁标记。若执行线程的同步代码执行结束,同样会释放锁标记,monitor 中的Owner标记赋值为 null,且计数器赋值为 0 计算。
在 Java 中,同步锁是可以重入的。只有同一线程调用同步方法或执行同步代码块,对同一个对象加锁时才可重入。当线程持有锁时,会在 monitor 的计数器中执行递增计算,若当前线程调用其他同步代码,且同步代码的锁对象相同时,monitor 中的计数器继续递增。每个同步代码执行结束,monitor 中的计数器都会递减,直至所有同步代码执行结束,monitor 中的计数器为 0 时,释放锁标记,_Owner 标记赋值为 null。