# IT明星不是梦 #synchronized 底层实现原理

SynchronizedJava并发编程的基础,也是面试的高频考点。

它的用法很独特,每一个对象都可以作为锁,很多人就很疑惑,它到底锁定的是什么呢?

image.png

要想真正弄明白这个问题就需要深入到Synchronized的实现机制,下面我用最易懂的方式来解析一下这个最难懂的关键字

Synchronized被称为同步锁,它的作用是保证一段代码在同一时间只能被一个线程执行,它是如何做到的呢?

synchronized的实现依赖虚拟机,我们在深入探讨Synchronized之前先来看一下jvm里面的一个重要概念:

对象头java中的对象在内存中存储的时候有一个重要的组成部分,就是对象头,对象头中主要包括两部分数据:类型指针和标记字段,通过类型指针可以知道该对象是什么类型的,标记字段用来存储对象运行时的数据,其中就有锁对象的指针,它是我们理Synchronized的关键,因为Synchronized使用到了各种锁。

标记字段中的锁对象指针指向了一个monitor对象,每个对象都有一个对应的monitor对象,monitor对象是同步工具,线程在执行加了Synchronized的代码段的时候要先去获取对象的monitor,执行完毕释放monitor。此过程是互斥的,一次只能有一个线程获取monitor,只有该线程释放monitor以后其它线程才能再获取它

image.png

例如:

public class MonitorDemo implements Runnable {

 int num = 0;

 Object o = new Object();

 @Override

 public void run() {

 synchronized (o) {

 num++;

 System.out.println(num);

 }

 }

}

线程执行到“synchronized(o)”时获取到o对象的monitor进入同步代码块执行:

num++;

System.out.println(num);

此时线程二执行到“synchronized(o)”就无法获取o对象的monitor,只能等到线程一执行完同步代码块,将omonitor释放才能获取到omonitor,进入同步代码块执行。

当对某一段代码加上Synchronized关键字以后这段代码虽然拥有了线程安全性,但是效率也明显下降,如何在两者之间寻求平衡,在java1.6之后Synchronized进行了一些优化,引入了偏向锁、轻量级锁、重量级锁。

1. 偏向锁:在很多情况下,虽然我们加了Synchronized关键字,保证了它的线程安全性,但是在实际当中多数情况下只有一个线程运行这段代码,并没有其它线程来和它竞争。这一个线程每次执行代码都需要获取锁和释放锁这个开销是没有必要的。所以引入了偏向锁,偏向锁在一个线程第一次访问的时候将该线程的id记录下来,下次判断如果还是该线程就不会加锁了。如果有另一个线程也来访问它,说明有可能出现线程并发。此时偏向锁就会升级为轻量级锁。

image.png

2. 轻量级锁:如果有其它线程也来访问这段代码,偏向锁会升级为轻量级锁,此时的线程是交替执行被加锁的代码段,一旦另一个线程需要同时执行加锁的代码段,就会形成竞争,首先会自旋,一段时间后轻量级锁升级为重量级锁。

3.重量级锁:重量级锁也就是文章开始提到的通过monitor对象来实现的,我们先看下重量级锁的执行过程当一段代码被上锁以后同一时间只能允许一个线程(线程一)执行,此时如果有另一个线程(线程二)也要执行这段代码就会被阻塞,挂起,线程一执行完后,线程二还会被唤醒,这些动作都是操作系统级别的处理,很消耗资源。

在轻量级锁中提到过一个概念,自旋,我们来看下自旋是怎么回事,当线程二发现无法执行该代码的时候它并不会挂起而是开始自旋,也就是在那里等待,不断的尝试执行,该操作会消耗cpu.自旋因为线程不需要被挂起和唤醒,执行速度很快,但是此时cpu在不停的运转,所以吞吐量会较低。

从上面的描述中我们可以看出锁升级的顺序:

image.png


猜你喜欢

转载自blog.51cto.com/11583017/2469549