Synchornized

image.png

Synchornized做了哪些优化?

synchornized底层都是使用monitorenter和monitorexit指令实现的,进入同步块就意味着拿到了monitorenter的所有权,而持有这个monitorenter所有权的线程就可以执行代码块,没有持有的线程就都在外面等着,早期JDK1.6之前都是使用阻塞线程的方式等待锁的释放,但很多轻量级的同步块,迅速执行完的代码块来说,这个锁就太重了,所以就在重量级锁的基础上引入了偏向锁和轻量级锁这两个状态,如果当前的synchornized加锁的情况下面,他这个偏向锁,是当前的对象没有竞争,而且总是由同一个线程获得锁的时候就会进入偏向锁的状态,一旦产生了竞争就会膨胀为轻量级锁,如果轻量级锁自旋超过了一定次数又会膨胀为重量级锁。

synchronized的执行过程:

  1. 检测Mark Word里面是不是当前线程的ID,如果是,表示当前线程处于偏向锁
  2. 如果不是,则使用CAS将当前线程的ID替换Mard Word,如果成功则表示当前线程获得偏向锁,置偏向标志位1
  3. 如果失败,则说明发生竞争,撤销偏向锁,进而升级为轻量级锁。
  4. 当前线程使用CAS将对象头的Mark Word替换为锁记录指针,如果成功,当前线程获得锁
  5. 如果失败,表示其他线程竞争锁,当前线程便尝试使用自旋来获取锁。
  6. 如果自旋成功则依然处于轻量级状态。
  7. 如果自旋失败,则升级为重量级锁。

Monitor 原理

每个 Java 对象都可以关联一个Monitor对象,如果使用 synchronized 给对象上锁(重量级)之后,该对象头的
Mark Word 中就被设置指向Monitor 对象的指针。
Monitor 结构如下:


image.png

刚开始 Monitor 中 Owner 为 null。
当 Thread-2 执行 synchronized(obj) 就会将 Monitor 的所有者 Owner 置为 Thread-2,Monitor中只能有一个Owner。
在 Thread-2上锁的过程中,如果 Thread-3,Thread-4,Thread-5 也来执行 synchronized(obj),就会进入
EntryList BLOCKED。
Thread-2 执行完同步代码块的内容,然后唤醒EntryList 中等待的线程来竞争锁,竞争的时是非公平的。
图中 WaitSet 中的 Thread-0,Thread-1 是之前获得过锁,但条件不满足进入调用wait()方法进入 WAITING 状态的线程。当调用notifyAll()方法之后,WaitSet中的 Thread-0,Thread-1 进入EntryList BLOCKED。

自适应自旋锁

自旋锁优点在于它避免一些线程的挂起和恢复操作,因为挂起线程和恢复线程都需要从用户态转入内核态,这个过程是比较慢的,所以通过自旋的方式可以一定程度上避免线程挂起和恢复所造成的性能开销
但是,如果长时间自旋还获取不到锁,那么也会造成一定的资源浪费,所以我们通常会给自旋设置一个固定的值来避免一直自旋的性能开销
自适应自旋锁是指,线程自旋的次数不再是固定的值,而是一个动态改变的值。简单来说,如果线程自旋成功了,则下次自旋的次数会增多,如果失败,下次自旋的次数会减少。

上面几种锁都是JVM自己内部实现,当我们执行synchronized同步块的时候jvm会根据启用的锁和当前线程的争用情况,决定如何执行同步操作;

你可能感兴趣的:(Synchornized)