目录
1. synchronized 的特性
1.1 互斥性
何为互斥性?
简单来说,就是别人在用,你想用就只能等别人用完才行。
对程序来说,当我们多个线程同时对一个锁对象进行加锁,第一个线程先完成加锁操作,第二个对象想要加锁,就必须等第一个线程将锁释放之后才能继续进行加锁操作。
1.2 可重入性
从字面意思来理解,就是可以多次进入,转换到代码中,就相当于同一个线程可以对一个锁对象进行多次加锁。
关于可重入性,其实在锁的内部有 : 线程拥有者 以及 计数器 。
某个线程对锁对象进行加锁,这个锁就会绑定这个线程,当这个线程重复对这个锁加锁时, 计数器就会 ++ ,解锁就会 -- , 当我们的计数器 减小到0 时候,这个锁才会彻底释放。
2. synchronized 的使用
2.1 对某个操作进行加锁
Object locker=new Object();
synchronized(locker){
count++;
}
上述我们是对 count ++ 这个操作进行加锁,当我们使用的时候,不让其他线程进行使用。
2.2 对方法进行加锁
public class Main{
public synchronized void Add1(int a,int b){
}
public void Add2(int a,int b){
synchronized(this){
}
}
public void Add3(int a,int b){
synchronized(Main.class){
}
}
}
我们在对方法进行加锁的时候,有两种加锁写法,可以直接在方法上进行加锁,也可以在方法内部对 调用该方法的对象 进行加锁 。
3. synchronized的锁机制
3.1 锁升级
当我们使用 synchronized 进行加锁时, 它不是一个固定的锁状态。
当我们刚开始进行加锁时,系统并不会先 进行加锁 ,而是先做个标记 ; 当出现 锁冲突 时,锁就会自动升级成 自旋锁(轻量级锁),这时才成功上锁 ; 当锁冲突进一步加剧时,锁就会继续升级到 重量级锁 。
锁升级的实质,其实是 性能 与 线程安全 之间的权衡。
3.2 锁消除
这是一种编译器优化的手段。
编译器会自动针对当前你所编写的 加锁代码,做出判断,如果这个场景下不需要加锁,编译器就会优化掉加锁这个操作。
当然,这个操作只会在 编译器非常有把握的时候才会进行锁消除。(触发概率不算高)
3.3 锁粗化
锁的粒度:
synchronized 的代码块中,代码越多,就认为 锁的粒度越大;反之,越细。
当我们频繁的进行加锁操作,就会消耗更多的cpu资源。不如直接对整个操作进行加锁,让 锁的粒度变粗,能够并发执行的逻辑就会变得更多,也能充分利用 多核cpu资源。
实际开发过程中,使⽤细粒度锁,是期望释放锁的时候其他线程能使⽤锁.
但是实际上可能并没有其他线程来抢占这个锁.这种情况JVM就会⾃动把锁粗化,避免频繁申请释放锁.