前言
Java中获取锁有两种方式,一种是使用synchronized关键字,另外一种就是使用Lock接口的实现类。前者就是Java原生的方式,但在优化以前(JDK1.6)性能都不如Lock,因为在优化之前一旦使用synchronized就会发生系统调用进入**内核态**,所以性能很差,也因此大神Doug Lea自己写了一套并发类,也就是JUC,并在JDK1.5版本引入进了Java类库。那么作为Java的亲儿子synchronized自然也不能示弱啊,所以sun公司对其做了大量的优化,引入了**偏向锁**、**轻量级锁**、**重量锁**、**锁消除**、**锁粗化**,才使得synchronized性能大大提升。
对象头
synchronized是悲观锁,在操作同步资源之前需要给同步资源先加锁,这把锁就是存在Java对象头里的,以Hotspot虚拟机为例,Hotspot的对象头主要包括两部分数据:Mark Word(标记字段)、Klass Pointer(类型指针)。
- Mark Word:默认存储对象的HashCode,分代年龄和锁标志位信息。这些信息都是与对象自身定义无关的数据,所以Mark Word被设计成一个非固定的数据结构以便在极小的空间内存存储尽量多的数据。它会根据对象的状态复用自己的存储空间,也就是说在运行期间Mark Word里存储的数据会随着锁标志位的变化而变化。
- Klass Point:对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。
锁的状态
锁的状态一共有四种:
- 无锁状态,无锁不可偏向(01)
- 偏向锁,无锁可偏向(01)
- 轻量级锁(00)
- 重量级锁(10)
无锁
一个对象没有被任何线程当做锁去使用,就是无锁状态。即无锁没有对资源进行锁定,所有的线程都能访问并修改同一个资源,但同时只有一个线程能修改成功。
无锁的特点就是修改操作在循环内进行,线程会不断的尝试修改共享资源。如果没有冲突就修改成功并退出,否则就会继续循环尝试。如果有多个线程修改同一个值,必定会有一个线程能修改成功,而其他修改失败的线程会不断重试直到修改成功。
此时我们创建一个a对象,查看它的锁状态为无锁01:
A a = new A();
String str = ClassLayout.parseInstance(a).toPrintable();
System.out.println(str); //01无锁状态
偏向锁
大部分情况下,锁不仅仅不存在多线程竞争,而是总是由同一个线程多次获得, 那么该线程会自动获取锁,降低获取锁的代价。
当一个线程访问加了同步锁的代码块时,会在对象头中存储当前线程的 ID,后续这个线程进入和退出这段加了同步锁的代码块时,不需要再次加锁和释放锁,而是直接比较对象头里面是否存储了指向当前线程的偏向锁。 如果相等表示偏向锁是偏向于当前线程的,就不需要再尝试获得锁了。
轻量级锁
一个对象如果被一个线程当做锁去使用,就是轻量级锁状态。其他线程会通过自旋的形式尝试获取锁,不会阻塞,从而提高性能。每个线程每次加锁和释放锁都会使用CAS去操作。
此时我们把上面的a作为锁对象来使用,可以发现锁状态为轻量级锁00:
new Thread(){
public void run() {
//把a作为锁对象
synchronized(a) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
Thread.sleep(10);
str = ClassLayout.parseInstance(a).toPrintable();
System.out.println(str);//00 轻量级锁
重量级锁
一个对象当做锁被一个线程持有,另外一个线程还继续去获取该锁,就是重量级锁**。**此时该线程线程获取到锁进入同步块,在没有释放锁之前,会阻塞其他未获取锁的线程。
此时我们在a锁对象被上面的线程持有时,继续去获取该锁对象,可以发现锁状态为重量级锁10:
new Thread(){
public void run() {
//把a作为锁对象
synchronized(a) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
Thread.sleep(10);
str = ClassLayout.parseInstance(a).toPrintable();
System.out.println(str);//10 重量级锁
总结
一个对象如果没有被任何线程当做锁去使用,就是无锁状态。
一个对象如果被一个线程当做锁去使用,就是轻量级锁状态。
一个对象当做锁被一个线程持有,另外一个线程还继续去获取该锁,就是重量级锁。
版权归原作者 初见# 所有, 如有侵权,请联系我们删除。