前言
昨天听取了某大咖直播视频,受益匪浅,之前也陆续了解过一些,但一直迷惑,昨天解惑,于是记录下来。接下来所有内容均针对64位操作系统进行分析。
1.JAVA对象头
在《深入了解JAVA虚拟机》中有一段内容,描述的正是JAVA对象构成,其中对象头由MarkWord、Classpointer、实例数据(对象属性)、对象对其补充、以及数组长度(数组对象才有)构成。每个JAVA对象都有一个通用的对象头结构,里面包含了对象布局、GC状态、加锁状态、hashcode,本文也将通过synchronized关键字分析对象的偏向锁、轻量级锁、重量级锁,以及锁降级,锁升级。classpointer包含了对象指针,指向该实例是属于哪一个类的实例。
在64位操作系统中,对象头由64位12个字节组成,其中markword占8字节,classpointer占4字节。
1.2怎样查看一个对象的对象头
OpenJDK提供了一个工具JOL,让我们能够清晰的了解到对象的对象头构成。通过maven直接引入相关依赖即可
<!-- https://mvnrepository.com/artifact/org.openjdk.jol/jol-core -->
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.10</version>
</dependency>
具体对象头信息如下示例代码:
public class T {
public static void main(String[] args) {
MarkWord markWord = MarkWord.builder().age(11).sex(true).price(22.2).build();
System.out.println(ClassLayout.parseInstance(markWord).toPrintable());
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
static class MarkWord{
private Integer age;
private boolean sex;
private double price;
}
}
运行结果如下:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 c2 00 f8 (01000011 11000010 00000000 11111000) (-134167997)
12 1 boolean MarkWord.sex true
13 3 (alignment/padding gap)
16 8 double MarkWord.price 22.2
24 4 java.lang.Integer MarkWord.age 11
28 4 (loss due to the next object alignment)
Instance size: 32 bytes
Space losses: 3 bytes internal + 4 bytes external = 7 bytes total
由运行结果可以看出,该对象实例一共占据了32字节
在java中boolean占据1个字节,double 8个字节,Integer4个字节,该对象实例中markword8个字节,classpointer4字节,因此需要填充7个字节,才能满足最终对象实例大小是8的倍数,能被8整除。(HotSpot虚拟机要求对象起始地址必须是8bit的整数倍)。
若是删除对象MarkWord的属性,重新运行,可以得到如下的结果,此时可以发现,对象只有16字节,为8的整数倍。而填充对齐只有4字节,因为markword8字节,classpointer4字节,需要凑齐8字节的整数倍,所以需要填充4字节。(填充对齐因此也可以理解为占位符)。
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 c2 00 f8 (01000011 11000010 00000000 11111000) (-134167997)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
接下来,解惑,到底哪一段是对象中的hashcode呢?改良过后的代码如下:
public class T2 {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
System.out.println("before hash");
MarkWord2 markWord = MarkWord2.builder().age(11).price(22.22).sex(true).build();
//未计算hash之前的对象头
System.out.println(ClassLayout.parseInstance(markWord).toPrintable());
//JVM计算的hashcode
System.out.println("JVM calculated hashcode---------0x"+Integer.toHexString(markWord.hashCode()));
HashUtil.countHash(markWord);
System.out.println("after hash");
//计算hashcode之后的对象头
System.out.println(ClassLayout.parseInstance(markWord).toPrintable());
}
}
然后我们创建一个HashUtil类,用于将对象头的56个bit的值转为16进制的hashcode
import sun.misc.Unsafe;
import java.lang.reflect.Field;
public class HashUtil {
public static void countHash(Object object) throws NoSuchFieldException, IllegalAccessException {
// 手动计算HashCode
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
Unsafe unsafe = (Unsafe) field.get(null);
long hashCode = 0;
//!!!注意,由于是小端存储,我们将从后往前计算
for (long index = 7; index > 0; index--){
//取Mark Word中的每一个Byte进行计算
hashCode |= (unsafe.getByte(object, index) & 0xFF) << ((index -1) *8);
}
String code = Long.toHexString(hashCode);
System.out.println("HashUtil calculated hashcode ---0x" + code);
}
}
运行结果如下:
before hash
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 d8 00 f8 (01000011 11011000 00000000 11111000) (-134162365)
12 1 boolean MarkWord2.sex false
13 3 (alignment/padding gap)
16 8 double MarkWord2.price 0.0
24 4 java.lang.Integer MarkWord2.age null
28 4 (loss due to the next object alignment)
Instance size: 32 bytes
Space losses: 3 bytes internal + 4 bytes external = 7 bytes total
JVM calculated hashcode---------0x3389e
HashUtil calculated hashcode ---0x0
after hash
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 89 99 ee (00000001 10001001 10011001 11101110) (-291927807)
4 4 (object header) 3f 00 00 00 (00111111 00000000 00000000 00000000) (63)
8 4 (object header) 43 c2 00 f8 (01000011 11000010 00000000 11111000) (-134167997)
12 1 boolean MarkWord2.sex true
13 3 (alignment/padding gap)
16 8 double MarkWord2.price 22.22
24 4 java.lang.Integer MarkWord2.age 11
28 4 (loss due to the next object alignment)
Instance size: 32 bytes
Space losses: 3 bytes internal + 4 bytes external = 7 bytes total
有前面的实例可以得出,对象头一共占据了12个字节,其中markword占据了8个字节,classpointer占据了4个字节。markword包含了锁信息,年龄带,hashcode、gc信息、对象布局等。那么,经过上面的例子通过调用hashcode前后(通过两种方法分别进行hashcode计算,得出来的hashcode一致),比较markword的数据,发现后面7字节,56位保存的才是hashcode数据(有变化的是后面的部分,56字节,7位, 7x8=56)
得出结论,第一个字节当中的8bit分别存储的就是年龄分带、偏向锁状态、对象状态、这些数据
2.偏向锁
关于对象的状态,一共有五种,无锁、偏向锁、轻量级锁(自旋锁)、重量锁、GC标记,然而对象状态只有两个位,因此最多只有00、01、10、11。那么我们要如何用2个bit表示的4个状态呢?很简单JVM通过把偏向锁和无锁表示为同一状态即都是01,然后通过前一个bit的偏向锁标示来分辨到底是无锁还是偏向锁。
运行结果如下:
-----加锁之前-----
# WARNING: Unable to get Instrumentation. Dynamic Attach failed. You may add this JAR as -javaagent manually, or supply -Djdk.attach.allowAttachSelf
# WARNING: Unable to attach Serviceability Agent. Unable to attach even with module exceptions: [org.openjdk.jol.vm.sa.SASupportException: Cannot run program "C:/Program Files/Java/jdk1.8.0_201/jre/bin/java": CreateProcess error=206, 文件名或扩展名太长。, org.openjdk.jol.vm.sa.SASupportException: Cannot run program "C:/Program Files/Java/jdk1.8.0_201/jre/bin/java": CreateProcess error=206, 文件名或扩展名太长。, org.openjdk.jol.vm.sa.SASupportException: Cannot run program "C:/Program Files/Java/jdk1.8.0_201/jre/bin/java": CreateProcess error=206, 文件名或扩展名太长。]
demo.markword.MarkWord2 object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 c2 00 f8 (01000011 11000010 00000000 11111000) (-134167997)
12 1 boolean MarkWord2.sex false
13 3 (alignment/padding gap)
16 8 double MarkWord2.price 0.0
24 4 java.lang.Integer MarkWord2.age null
28 4 (loss due to the next object alignment)
Instance size: 32 bytes
Space losses: 3 bytes internal + 4 bytes external = 7 bytes total
加锁了.
demo.markword.MarkWord2 object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 80 eb 76 02 (10000000 11101011 01110110 00000010) (41347968)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 c2 00 f8 (01000011 11000010 00000000 11111000) (-134167997)
12 1 boolean MarkWord2.sex false
13 3 (alignment/padding gap)
16 8 double MarkWord2.price 0.0
24 4 java.lang.Integer MarkWord2.age null
28 4 (loss due to the next object alignment)
Instance size: 32 bytes
Space losses: 3 bytes internal + 4 bytes external = 7 bytes total
-----加锁之后-----
demo.markword.MarkWord2 object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 c2 00 f8 (01000011 11000010 00000000 11111000) (-134167997)
12 1 boolean MarkWord2.sex false
13 3 (alignment/padding gap)
16 8 double MarkWord2.price 0.0
24 4 java.lang.Integer MarkWord2.age null
28 4 (loss due to the next object alignment)
Instance size: 32 bytes
Space losses: 3 bytes internal + 4 bytes external = 7 bytes total
看锁状态,只需要看第一个字节,最后三位,倒数第三位为0代表非偏向锁 1代表偏向锁,倒数两位00代表轻量级锁,10重量级锁,01偏向锁。
很显然,在加锁的过程中,第一位的8个bit 00000000 后三位是000,表示是轻量级锁。在有且只有一个线程去获取synchronized中的对象的时候,该对象的锁为偏向锁,但是上面你的结果却不符合我们的预期,因为,JVM的偏向锁有延迟启动机制,每个对象并不会在一开始就获得偏向锁,需要程序启动后4-5秒才能够获得偏向锁。我们在上面的例子中加入线程休眠5秒(可以配置JVM 启动参数取消偏向锁延迟-XX:+UseBiasedLocking -XX:BiasedLockingStartupDelay=0)
import demo.markword.MarkWord2;
import org.openjdk.jol.info.ClassLayout;
import java.util.concurrent.TimeUnit;
public class LockDemo {
static MarkWord2 markWord2;
public static void main(String[] args) throws InterruptedException {
TimeUnit.SECONDS.sleep(5);
markWord2 = new MarkWord2();
System.out.println("-----加锁之前-----");
System.out.println(ClassLayout.parseInstance(markWord2).toPrintable());
sync();
System.out.println("-----加锁之后-----");
System.out.println(ClassLayout.parseInstance(markWord2).toPrintable());
}
private static void sync(){
synchronized(markWord2){
System.out.println("加锁了.");
System.out.println(ClassLayout.parseInstance(markWord2).toPrintable());
}
}
}
加入线程休眠代码后,运行结果如下:
-----加锁之前-----
# WARNING: Unable to get Instrumentation. Dynamic Attach failed. You may add this JAR as -javaagent manually, or supply -Djdk.attach.allowAttachSelf
# WARNING: Unable to attach Serviceability Agent. Unable to attach even with module exceptions: [org.openjdk.jol.vm.sa.SASupportException: Cannot run program "C:/Program Files/Java/jdk1.8.0_201/jre/bin/java": CreateProcess error=206, 文件名或扩展名太长。, org.openjdk.jol.vm.sa.SASupportException: Cannot run program "C:/Program Files/Java/jdk1.8.0_201/jre/bin/java": CreateProcess error=206, 文件名或扩展名太长。, org.openjdk.jol.vm.sa.SASupportException: Cannot run program "C:/Program Files/Java/jdk1.8.0_201/jre/bin/java": CreateProcess error=206, 文件名或扩展名太长。]
demo.markword.MarkWord2 object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 00 00 00 (00000101 00000000 00000000 00000000) (5)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 c2 00 f8 (01000011 11000010 00000000 11111000) (-134167997)
12 1 boolean MarkWord2.sex false
13 3 (alignment/padding gap)
16 8 double MarkWord2.price 0.0
24 4 java.lang.Integer MarkWord2.age null
28 4 (loss due to the next object alignment)
Instance size: 32 bytes
Space losses: 3 bytes internal + 4 bytes external = 7 bytes total
加锁了.
demo.markword.MarkWord2 object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 80 31 02 (00000101 10000000 00110001 00000010) (36798469)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 c2 00 f8 (01000011 11000010 00000000 11111000) (-134167997)
12 1 boolean MarkWord2.sex false
13 3 (alignment/padding gap)
16 8 double MarkWord2.price 0.0
24 4 java.lang.Integer MarkWord2.age null
28 4 (loss due to the next object alignment)
Instance size: 32 bytes
Space losses: 3 bytes internal + 4 bytes external = 7 bytes total
-----加锁之后-----
demo.markword.MarkWord2 object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 80 31 02 (00000101 10000000 00110001 00000010) (36798469)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 c2 00 f8 (01000011 11000010 00000000 11111000) (-134167997)
12 1 boolean MarkWord2.sex false
13 3 (alignment/padding gap)
16 8 double MarkWord2.price 0.0
24 4 java.lang.Integer MarkWord2.age null
28 4 (loss due to the next object alignment)
Instance size: 32 bytes
Space losses: 3 bytes internal + 4 bytes external = 7 bytes total
可以清晰地看到,在休眠五秒后,过了偏向锁的延迟时间,第一字节的8bit为00000101,后三位里,倒数第三位是1表示偏向锁
倒数两位为01表示偏向锁。此时锁状态成了偏向锁。和轻量级锁重量级锁不同的是,偏向锁每次退出后,状态仍然是偏向锁。
在单线程的情况下,使用synchronized并不会给被synchronized修饰的对象加锁,而只是在被修饰的对象对象头markdown中,将当前线程的线程ID赋予进去。
3.轻量级锁
轻量级锁同前面的实例
public class LockDemo {
static MarkWord2 markWord2;
public static void main(String[] args) throws InterruptedException {
// TimeUnit.SECONDS.sleep(5);
markWord2 = new MarkWord2();
System.out.println("-----加锁之前-----");
System.out.println(ClassLayout.parseInstance(markWord2).toPrintable());
sync();
System.out.println("-----加锁之后-----");
System.out.println(ClassLayout.parseInstance(markWord2).toPrintable());
}
private static void sync(){
synchronized(markWord2){
System.out.println("-----加锁了-----");
System.out.println(ClassLayout.parseInstance(markWord2).toPrintable());
}
}
}
此时,去掉线程休眠的代码,执行结果如下
-----加锁之前-----
# WARNING: Unable to get Instrumentation. Dynamic Attach failed. You may add this JAR as -javaagent manually, or supply -Djdk.attach.allowAttachSelf
# WARNING: Unable to attach Serviceability Agent. Unable to attach even with module exceptions: [org.openjdk.jol.vm.sa.SASupportException: Cannot run program "C:/Program Files/Java/jdk1.8.0_201/jre/bin/java": CreateProcess error=206, 文件名或扩展名太长。, org.openjdk.jol.vm.sa.SASupportException: Cannot run program "C:/Program Files/Java/jdk1.8.0_201/jre/bin/java": CreateProcess error=206, 文件名或扩展名太长。, org.openjdk.jol.vm.sa.SASupportException: Cannot run program "C:/Program Files/Java/jdk1.8.0_201/jre/bin/java": CreateProcess error=206, 文件名或扩展名太长。]
demo.markword.MarkWord2 object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 d8 00 f8 (01000011 11011000 00000000 11111000) (-134162365)
12 1 boolean MarkWord2.sex false
13 3 (alignment/padding gap)
16 8 double MarkWord2.price 0.0
24 4 java.lang.Integer MarkWord2.age null
28 4 (loss due to the next object alignment)
Instance size: 32 bytes
Space losses: 3 bytes internal + 4 bytes external = 7 bytes total
-----加锁了-----
demo.markword.MarkWord2 object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 10 ee 69 02 (00010000 11101110 01101001 00000010) (40496656)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 d8 00 f8 (01000011 11011000 00000000 11111000) (-134162365)
12 1 boolean MarkWord2.sex false
13 3 (alignment/padding gap)
16 8 double MarkWord2.price 0.0
24 4 java.lang.Integer MarkWord2.age null
28 4 (loss due to the next object alignment)
Instance size: 32 bytes
Space losses: 3 bytes internal + 4 bytes external = 7 bytes total
-----加锁之后-----
demo.markword.MarkWord2 object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 d8 00 f8 (01000011 11011000 00000000 11111000) (-134162365)
12 1 boolean MarkWord2.sex false
13 3 (alignment/padding gap)
16 8 double MarkWord2.price 0.0
24 4 java.lang.Integer MarkWord2.age null
28 4 (loss due to the next object alignment)
Instance size: 32 bytes
Space losses: 3 bytes internal + 4 bytes external = 7 bytes total
Disconnected from the target VM, address: '127.0.0.1:56857', transport: 'socket'
一开始,由于偏向锁加载延迟,第一字节的8位为00000001 倒数第三位为0,代表非偏向锁,但是后两位是01 代表一开始是无锁。后面加锁的时候成了00010000 变成了轻量级锁,执行完成后 00000001 又成了无锁。偏向锁如果有线程争用,就会升级为自旋锁(轻量级锁)。当自旋锁自旋十次(或CPU数量一半)就会升级为重量级锁。
4.重量级锁
在多线程并发争抢锁的情况下,轻量级锁升级成了重量级锁
import demo.markword.MarkWord2;
import org.openjdk.jol.info.ClassLayout;
public class ZhongLockDemo {
static MarkWord2 markWord = null;
public static void main(String[] args) {
markWord = new MarkWord2();
System.out.println("before lock");
System.out.println(ClassLayout.parseInstance(markWord).toPrintable());
new Thread(()->{
sync();
}).start();
new Thread(()->{
sync();
}).start();
System.out.println("after lock");
System.out.println(ClassLayout.parseInstance(markWord).toPrintable());
}
public static void sync(){
synchronized (markWord){
System.out.println(Thread.currentThread().getName()+" is locking");
System.out.println(ClassLayout.parseInstance(markWord).toPrintable());
}
}
}
before lock
# WARNING: Unable to get Instrumentation. Dynamic Attach failed. You may add this JAR as -javaagent manually, or supply -Djdk.attach.allowAttachSelf
# WARNING: Unable to attach Serviceability Agent. Unable to attach even with module exceptions: [org.openjdk.jol.vm.sa.SASupportException: Cannot run program "C:/Program Files/Java/jdk1.8.0_201/jre/bin/java": CreateProcess error=206, 文件名或扩展名太长。, org.openjdk.jol.vm.sa.SASupportException: Cannot run program "C:/Program Files/Java/jdk1.8.0_201/jre/bin/java": CreateProcess error=206, 文件名或扩展名太长。, org.openjdk.jol.vm.sa.SASupportException: Cannot run program "C:/Program Files/Java/jdk1.8.0_201/jre/bin/java": CreateProcess error=206, 文件名或扩展名太长。]
demo.markword.MarkWord2 object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 c2 00 f8 (01000011 11000010 00000000 11111000) (-134167997)
12 1 boolean MarkWord2.sex false
13 3 (alignment/padding gap)
16 8 double MarkWord2.price 0.0
24 4 java.lang.Integer MarkWord2.age null
28 4 (loss due to the next object alignment)
Instance size: 32 bytes
Space losses: 3 bytes internal + 4 bytes external = 7 bytes total
Thread-1 is locking
after lock
demo.markword.MarkWord2 object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 1a df fd 04 (00011010 11011111 11111101 00000100) (83746586)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 c2 00 f8 (01000011 11000010 00000000 11111000) (-134167997)
12 1 boolean MarkWord2.sex false
13 3 (alignment/padding gap)
16 8 double MarkWord2.price 0.0
24 4 java.lang.Integer MarkWord2.age null
28 4 (loss due to the next object alignment)
Instance size: 32 bytes
Space losses: 3 bytes internal + 4 bytes external = 7 bytes total
demo.markword.MarkWord2 object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 1a df fd 04 (00011010 11011111 11111101 00000100) (83746586)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 c2 00 f8 (01000011 11000010 00000000 11111000) (-134167997)
12 1 boolean MarkWord2.sex false
13 3 (alignment/padding gap)
16 8 double MarkWord2.price 0.0
24 4 java.lang.Integer MarkWord2.age null
28 4 (loss due to the next object alignment)
Instance size: 32 bytes
Space losses: 3 bytes internal + 4 bytes external = 7 bytes total
Thread-2 is locking
demo.markword.MarkWord2 object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 1a df fd 04 (00011010 11011111 11111101 00000100) (83746586)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 c2 00 f8 (01000011 11000010 00000000 11111000) (-134167997)
12 1 boolean MarkWord2.sex false
13 3 (alignment/padding gap)
16 8 double MarkWord2.price 0.0
24 4 java.lang.Integer MarkWord2.age null
28 4 (loss due to the next object alignment)
Instance size: 32 bytes
Space losses: 3 bytes internal + 4 bytes external = 7 bytes total
总结:在一开始,只有单线程的情况下,一个线程过来,会先把自己的线程ID赋予到对象头里的markword里,表示我拿到了这个对象了,这个时候对应的锁是偏向锁,但是,JVM对于单线程的情况下,偏向锁有延迟。
当有线程竞争,这个时候,偏向锁会变成轻量级锁,其实,轻量级锁就是运用旋转,CAS,同时把自己的LR(LocalRecord放到线程栈),这个时候变成了轻量级锁,当然,这个情况下,不可能是无限自旋的,因为几个线程,对于CPU来说,压力其实并不大,但是假设是几千几万个线程,一直阻塞,不断自旋,其实CPU的压力是非常大的,当然不会让他这么干, 二十当自选次数超过十次,或超过CPU个数一半的情况下,这个时候,会升级锁成重量级锁,同时,会把该线程挂到该一个阻塞队列里面,等到真正持有该锁的线程执行完毕,释放锁后,出队,重新争抢资源。
synchronized对于锁的运用,体现在以下几个方面
1:代码层面,synchronized对对象或代码块进行加锁操作
2.底层 其实是一个MonitorEnter和MonitorExit的操作
3.旋转 ,锁升级
对于自旋锁,示例如下,JAVA提供了Atomic类,用于提供一系列原子操作
AtomicInteger in = new AtomicInteger();
in.incrementAndGet();
/**
* Atomically increments by one the current value.
*
* @return the updated value
*/
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
自旋,底层其实调用的是native方法,涉及到汇编相关的问题,但是,不论怎样,最最核心,涉及到的不过也就是用户态、内核态、缓存行、缓存对齐相关的东西,旋转运用到的最核心的就是lock指令和compare指令,lock才是核心,compare是非原子的操作。
补充:
到底什么时候锁会降级呢?
锁降级没什么太大的意义,因为锁降级发生在GC的时候,GC的时候,对象都即将被回收,没用了,所以说锁降级没什么太大的意义。 由于自旋锁不占用CPU,如果加锁代码执行时间短,线程数量少用自旋锁,如果执行时间长,执行数量多的代码,用系统锁。