1、描述synchronized和reentrantlock的底层实现及重入的底层原理
1.1 CAS
1、compare and swap:比较并且交换
没有锁的状态下,保证多个线程对一个锁的更新
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;
}
2、图解原理:
3、ABA问题:使用A时,其他线程中间改回B但在实际操作的时候又改回了A(可以加版本号,进行对比值和版本号)
4、汇编指令:lock cmpxchg
1.2 synchronized
1、锁有一个升级过程:
无锁 --(1)–> 偏向锁 --(2)–> 轻量级锁(自旋锁、自适应自旋) --(3)–> 重量级锁
(1)上厕所,门口贴标签(第一次加锁)
对象头上的某两位,指定锁的什么类型的锁,偏向锁记录这个线程的id值,我们就认为这个锁为这个线程独有,竞争,发生锁升级
(2)只要发生竞争(LR),就升级为轻量级锁(CAS自旋锁)
(3)竞争比较激烈时,向操作系统申请一把大锁,进程在等待队列中等待
2、锁降级
发生在GC状态,
3、锁消除:lock eliminate
void add() {
StringBuffer sb = new StringBuffer();
sb.append("li-").append("si");
System.out.println(sb.toString());
}
StringBuffer是线程安全的,因为他的方法被synchronized修饰,但上述例子中,sb这个引用只会在add方法中使用,不可能被其他线程引用,(因为是局部变量,栈私有)因此sb是不可能共享的资源,jvm会自动消除StringBuffer对象内部的锁
4、锁粗化:lock coarsening
String addStr() {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < 100; i++) {
sb.append(i);
}
return sb.toString();
}
jvm会监测到这样一连串的操作都会对同一个对象加锁,此时jvm就会将加锁范围粗化到这一连串操作的外部,使得这一连串的操作只需要加一次锁即可
5、JIT:just in time compiler----> 热点代码直接编译成机器语言
6、实现过程:
(1)Java代码:synchronized
(2)字节码指令:MONITORENTER MONITOREXIT
(3)执行过程中自动升级
(4)更底层实现:lock cmpxchg
2、Object o = new Object();在内存中占有的字节数
JOL = java object layout
2.1 对象在内存中的分布:
2.2 markword+类型指针 -> 对象头
markword -> 锁的信息 (8字节)
class pointer -> 属于哪个class (4字节)
padding:当对象整体的字节数不可以被8整除的时候,使用padding补齐 -> 提高效率
2.3 结果:
new object 16字节(补齐 压缩之后的 8+4)不压缩的话就是8+8
Object o 引用 4个字节
3、volatile
3.1 基本概念
1、保证线程可见性
public class VolatileCreate {
volatile boolean flag = true; // begin --
//end --
// boolean flag = true; // begin --
void run() {
System.out.println(" begin -- ");
while (flag){
}
System.out.println(" end -- ");
}
public static void main(String[] args) {
VolatileCreate vc = new VolatileCreate();
new Thread(()->{
vc.run();
}).start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
vc.flag = false;
}
}
2、阻止指令重排序:
(1)乱序执行:读指令的同时可以同时执行不影响的其他指令
写的同时可以进行合并写
这样CPU执行就是乱序的
(2)volatile如何解决指令重排序
volatile
ACC_VOLATILE – 字节码层级
JVM内存屏障
hotspot实现
3.2 缓存行概念:
1、cache line的概念,缓存行对齐 伪共享 - - - - - > 缓存行一致性协议
2、读数据 按块来读(8字节)
单独占一个缓存行,就不需要重复读取数据,提升性能
3.3 系统底层如何实现数据一致性
1、MESI如果能解决,就使用MESI
2、锁总线
3.4 系统底层如何保证有序性
1、sfence mfence ifence等系统原语
2、锁总线
3.5 内存屏障
屏障两边的指令不可以重排,保证有序
1、JSR内存屏障:
3.6 volatile的实现细节
1、编译器层面
加了一个volatile关键字
2、JVM层面
4、ThreadLocal(线程本地变量)
4.1 定义:
是一个线程内部的存储类,可以在指定线程内存储数据,数据存储以后,只有指定线程可以得到存储数据(只有本线程可以读到,其他线程读不到)
想象成容器,里面可以装对象
4.2 代码理解
public class ThreadLocalTest {
static ThreadLocal<Person> tl = new ThreadLocal<Person>();
public static void main(String[] args) {
new Thread(()->{
tl.set(new Person());
}).start();
new Thread(()->{
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(tl.get()); // 取不到
}).start();
}
}
class Person{
String name = "lisi";
public Person() {
System.out.println(name);
}
}
4.2 @Trasactional (事务)
本质上是ThreadLocal实现的,从ThreadLocal中取conn,一定是同一个,与线程绑定
5、强软弱虚—Java中的引用类型
5.1 虚引用
get不到
管理堆外内存(操作系统内存)
当对象被回收时,通过Queue可以检测到,然后清理堆外内存
gc、队列
5.2 弱引用
未回收时get到
经过垃圾回收,弱引用指向的对象直接被回收了
NIO依据次实现
5.3 强引用
正常的引用,没有引用的话就被垃圾回收器回收