早期synchronized:
Synchronized是通过对象内部的一个叫做监视器锁(monitor)来实现的。但是监视器锁本质又是依赖于底层的操作系统的Mutex Lock来实现的。而操作系统实现线程之间的切换这就需要从用户态转换到核心态,这个成本非常高,状态之间的转换需要相对比较长的时间,这就是为什么Synchronized效率低的原因。
这种依赖于操作系统Mutex Lock所实现的锁被称之为“重量级锁”。JDK中对Synchronized做的种种优化,其核心都是为了减少这种重量级锁的使用。JDK1.6以后,为了减少获得锁和释放锁所带来的性能消耗,提高性能,引入了“偏向锁”和“轻量级锁”。
synchronized的类别:
根据获取锁的分类:获取对象锁和获取类锁
获取对象锁的两种用法:
修饰代码块,指定的是实例对象(this, object)
修饰非静态方法,锁是当前对象的实例对象
获取类锁的两种用法:
修饰代码块,指定的是类对象(XX.class)
修饰静态方法,锁是当前类对象
synchronized锁的不是代码,锁的都是对象
关于对象在内存中的布局:
对象头
实例数据
对齐填充
其中对象头的结构:
Mark Word
(主要研究重量级锁,也就是常说的synchronized锁)
要分析synchronized锁,就要了解Monitor这个java对象天生就自带的锁
当多个线程来竞争一个对象锁时,首先会进入到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.类锁和对象锁互不干扰