请描述synchronized和reentrant lock的底层实现,以及重入的底层原理
synchronized
java 代码级别的代码实现 synchronized
class文件 中通过 monitorenter monitorexit 两条Jvm的汇编指令实现
Jvm执行过程中完成锁升级过程
汇编实现 通过 一条汇编指令 lock cmpxchg
reentrant lock
请描述锁的四种状态和升级过程
四种状态
无锁状态 ———> 偏向锁———>轻量级锁(aqs,自旋,无锁)———>重量级锁(线程队列)
升级过程
创建对象后一段时间自动升级成偏向锁
当有一个线程访问时将自己线程指针贴到markword中
当有两个线程竞争时升级成轻量级锁,jdk1.6以后添加了自适应自旋,通过aqs进行操作
当自旋次数过多,访问线程数量过多,生成重量级锁,向操作系统老大申请资源
CAS的ABA问题如何解决
version
请谈一下AQS,为什么AQS底层是CAS + volatile
AQS中的state 的volatile修饰的
在内部维护了一个队列(双向链表)在没有抢到锁的时候,向这个链表的尾部节点插值,需要进行cas操作
谈一下你对volatile 的理解
解决了线程之前可见性和禁止指令重排的维问题
volatile的可见性和禁止指令重排是如何实现的
禁止指令重排
在源码体现 volatile
在clas文件 ACC_VOLATILE 标记
在JVM层面 通过内存屏障
loadload
storestore
loadstore
storeload
在JVM规范中 要求虚拟机实现 在volatile操作前后加一个内存屏障
屏障两边的指令不可以进行重排序
hotsport实现
CAS是什么
compare and swap
又称无锁,自旋锁
请描述一下对象创建的过程
对象在内存中的内存布局
markwork 8个字节
classpointer 4个字节 这里4个字节是因为
data 成员变量
padding 4个字节 补充能被8乘除,提高读取速度
DCL单例为什么加volatile
防止指令重排,拿到半初始化的对象
/**
* @description: double check lock
* @program: lock-immortal
* @author: Mikael
* @date: 2021-08-04 10:37
**/
public class DCL {
private DCL(){}
private static DCL DCL_;
public static DCL getDcl(){
if (DCL_ == null){
synchronized (DCL.class){
if (DCL_ == null){
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
DCL_ = new DCL();
}
}
}
return DCL_;
}
public static void main(String[] args) {
for (int i = 0; i < 10_0000; i++) {
new Thread(()->{
System.out.println("DCL.getDcl().hashCode() = " + DCL.getDcl().hashCode());
}).start();
}
}
}
Object o = new Object()在内存种占了多少字节
答案:16个字节
导入jar
<!-- https://mvnrepository.com/artifact/org.openjdk.jol/jol-core -->
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.9</version>
</dependency>
public class CAS {
public static void main(String[] args) {
// AtomicInteger atomicInteger = new AtomicInteger();
Object o = new Object();
System.out.println(ClassLayout.parseInstance(o).toPrintable());
}
}
运行结果
java.lang.Object 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) e5 01 00 20 (11100101 00000001 00000000 00100000) (536871397)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
markwork 8个字节
classpointer 4个字节 这里4个字节是因为
padding 4个字节 补充能被8乘除,提高读取速度
java -XX:+PrintCommandLineFlags -version 通过这个参数,可以看见默认传递的参数
因为现在的系统都是64位的,所以一个指针也就是64位 8个字节
UseCompressedClassPointers 这个参数把8个字节压缩成了4个字节
UseCompressedOops 这个参数把普通的对象指针也压缩到4个字节
oop == ordinary object pointer
你了解Thread local吗? Thread local如何解决内存泄漏问题
线程本地变量
请描述一下锁的分类以及在JDK种的应用
强软弱虚应用类型
强引用 堆中对象没有引用指向时回收 normal
软引用 堆中数据满时,回收软引用 soft 一般用于缓存
弱引用 被GC扫描到就会被回收 weak 一般用于一次性数据
虚引用 管理堆外内存 phantom