早期synchronized:

Synchronized是通过对象内部的一个叫做监视器锁(monitor)来实现的。但是监视器锁本质又是依赖于底层的操作系统的Mutex Lock来实现的。而操作系统实现线程之间的切换这就需要从用户态转换到核心态,这个成本非常高,状态之间的转换需要相对比较长的时间,这就是为什么Synchronized效率低的原因。

这种依赖于操作系统Mutex Lock所实现的锁被称之为“重量级锁”。JDK中对Synchronized做的种种优化,其核心都是为了减少这种重量级锁的使用。JDK1.6以后,为了减少获得锁和释放锁所带来的性能消耗,提高性能,引入了“偏向锁”和“轻量级锁”。

 

synchronized的类别:

根据获取锁的分类:获取对象锁和获取类锁

获取对象锁的两种用法:

修饰代码块指定的是实例对象(this, object)

修饰非静态方法,锁是当前对象的实例对象

 

获取类锁的两种用法:

修饰代码块指定的是类对象(XX.class)

修饰静态方法,锁是当前类对象

synchronized锁的不是代码,锁的都是对象

 

关于对象在内存中的布局:

对象头

实例数据

对齐填充

 

其中对象头的结构:

Mark Word 

(主要研究重量级锁,也就是常说的synchronized锁)

 

要分析synchronized锁,就要了解Monitor这个java对象天生就自带的锁

http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/111b95ad4584/src/share/vm/runtime/objectMonitor.hpp

多个线程来竞争一个对象锁时,首先会进入到EntryList(锁池)等待占有当前对象锁的线程释放资源

其他线程释放资源后,获取锁,同时_count 计数器加1_owner 指向当前线程

如果当前线程执行wait方法,则释放锁,进入_WaitSet(等待池)_owner 置null_count 减1

 

流程示意图:

 

源码解析:

javap -verbose获取到反编译后的字节码来进行分析:

1、同步代码块

如果有其他线程先与当前线程拥有Object.monitor的所有权则会阻塞在monitorenter处(3)

如果能够获取,就继续执行,直到完成当前线程所有任务,释放Object.monitor的所有权执行monitorexit(13)

为了能够让monitorenter与monitorexit配对执行(避免Object.monitor的所有权一直得不到释放),有了17-22这个编译器自动生成的异常处理器,这个处理器能够处理所有产生的异常。

2、同步方法

在访问标志位这儿添加了ACC_SYNCHRONIZED,其控制方式是隐式的,不需要通过字节码控制

 

附加:对象锁和类锁

1.有线程访问对象的同步代码块时,另外的线程可以访问该对象的非同步代码块

2.若锁住的是同一个对象,一个线程在访问对象的同步代码块或同步方法时,另一个访问对象的同步代码块或同步方法的线程会被阻塞,如果另一个线程访问的是同步方法或者同步代码块,也会被阻塞

3.同一个类的不同对象的对象锁互不干扰

4.类锁也是一种特殊的对象锁,因此表现基本和类锁一致。由于一个类中只有一把类的对象锁,所以一个类的不同对象使用类锁将会是同步的

5.类锁和对象锁互不干扰