synchtonized锁住的不是代码,而是对象。
public class Demo {
private static int count = 10;
private static Object o = new Object();
public static void main(String[] args) {
//任何线程要执行下面的代码,必须先拿到 o 的锁
synchronized (o){
count--;
System.out.println(Thread.currentThread().getName() + " -> count -> " + count);
}
}
}
Synchronized特性,原子性,可重入。
可重入概念:当线程请求一个由其它线程持有的对象锁时,该线程会阻塞,
而当线程请求由自己持有的对象锁时,如果该锁是重入锁,请求就会成功,否则阻塞。
也就是同一个线程,在获取到锁以后,再次获取该锁,是可以拿到的,就是可重入。
字节码层级的实现
//字节码(.class文件)层级的实现
monitorenter //锁
//在此期间的指令被锁住
monitorexit //释放锁
Hotspot层级实现
什么是Hotspot
JVM是虚拟机,总的来说是一种标准规范,虚拟机有很多实现版本。主要作用就是运行java的类文件的。
而HotSpot是虚拟机的一种实现,这个目前看起来“血统纯正”的虚拟机在最初并非由Sun公司开发,
而是由一家名为“Longview Technologies”的小公司设计的;
甚至这个虚拟机最初并非是为Java语言而开发的,它来源于Strongtalk VM,
而这款虚拟机中相当多的技术又是来源于一款支持Self语言实现“达到C语言50%以上的执行效率”的目标而设计的虚拟机,
Sun公司注意到了这款虚拟机在JIT编译上有许多优秀的理念和实际效果,
在1997年收购了Longview Technologies公司,从而获得了HotSpot VM。
同时也是目前使用范围最广的虚拟机。
HotSpot,顾名思义,它是基于热点代码探测的,有JIT即时编译功能,能提供更高质量的本地代码。
二者区别是一个是标准,一个是实现方式。
synchronize锁升级过程
无锁态升级为偏向锁
无锁到偏向锁: 内容由hashcode、分代年龄、锁的状态标识转变为线程ID、epoch(过期)、分代年龄、锁标识。
如果有第一个线程进入到了synchronize代码块里了,它会经历一个锁升级的过程,从无锁状态升级到偏向锁。
为什么叫偏向锁,因为它是偏向于给第一个线程上锁,所以偏向于第一个线程。
它会检查,看你有没有上锁,看你是不是偏向锁,看你是否是第一次锁住这个对象。
也就是第一个线程到来,访问一个从来没有上过锁的对象。
然后它把自己的线程ID放在最前面,修改锁为偏向锁。也就是上图偏向锁里面包含的内容。
所以在并发量较大的情况下,关闭偏向锁的效率反而更高。
如何关闭偏向锁?JVM调优其中一个参数:
-XX:-UseBiasedLocking //冒号后面的减号代表关闭,加号代表打开。
偏向锁升级到自旋锁
偏向锁到轻量级锁: 内容由线程ID、epoch(过期)、分代年龄、锁标识转变为锁记录存入线程栈中(LockRecord)。
如果有第二个线程进入或者多个线程进入,相互竞争,它会升级为轻量级锁。
此时第一个线程会执行到一个安全点,和其他的线程公平去竞争轻量级锁,无锁态的内容全部记录到锁记录里面。
每一个轻量级锁,都会把markword复制到自己的锁记录里面。它有自己的线程栈,在栈里存放锁记录。
轻量级锁如果有任意一个线程发生自旋超过10次,或者整个自旋等待的线程超过cpu核数的一半,升级为重量级锁。
通过-XX:+PreBlockSpin来指定次数,默认是10
轻量级锁升级到重量级锁
重量级锁是要向操作系统申请的,所以它叫重量级锁。
Hotspot源码中会生成一个上图的monitor对象,可以把它理解为操作系统级别的一个互斥量(mutex),
当想要去拿重量级锁(操作系统级别)时,就不由JVM说了算了,这是由操作系统说了算的,因为JVM只是操作系统上的一个执行程序而已,
任何线程到这里,都是进入到一个队列进行排队,轮到你了才能拿到这把锁,才返回给用户态继续执行。
那么为什么自旋锁(轻量级锁)不用经过操作系统,那么它还要申请重量级锁呢?
因为自旋锁是消耗cpu资源的,就像死循环那样。
用户态 OR 内核态
内核态是操作系统层级的。是指操作系统内核提供的一些函数。
用户态是cpu层级的。升级重量级锁时,是要从用户态经过内核态申请,是要经过用户态到内核态的一个转换。
大概意思就是你自己的小锁不管用了,你得向你的上级去申请帮助了。这就是重量级锁它就重在了这里。
无锁态、偏向锁、轻量级锁,这3个形态是不用经过操作系统的,也就是不用惊动你的上级的。
只有升级到重量级锁的时候,才需要向操作系统去申请。这就是锁升级的过程。
GC标记
在GC过程中,很多的对象都需要复制和移动,在这个过程中,对它进行锁定,这是GC标记。表示这个标记正在被GC使用,其他人先不要动。
CPU层面实现
记住一条指令就行了
使用Lock comxchg(cmpxchg)实现,这是intel(因特尔)的实现,不同的cpu实现是不一样的。
comxchg一定是改变了内存里的某块区域,前面加上lock,指的是我在干一件事的时候,不会被其他cpu所打断。