synchronized的特性
1. 乐观锁/悲观锁自适应,开始时是乐观锁,如果锁冲突频繁,就转换为悲观锁
2.轻量级/重量级锁自适应 开始是轻量级锁实现,如果锁被持有的时间较长,就转换成重量级锁
3.自旋/挂起等待锁自适应
4.不是读写锁
5.非公平锁
6,可重入锁
synchronized的使用
1)直接修饰普通方法
锁的是对象(单个对象内部加锁):
public class SynchronizedDemo {
public synchronized void methond() {
}
}
(2)修饰静态方法
锁的是类的所有对象:
public class SynchronizedDemo {
public synchronized static void method() {
}
}
(3)修饰代码块
明确指定锁哪个对象:
锁当前对象:
public class SynchronizedDemo {
public void method() {
synchronized (this) {
}
}
}
锁类对象:
public class SynchronizedDemo {
public void method() {
synchronized (SynchronizedDemo.class) {
}
}
}
只有两个线程竞争同一把锁,才会有锁冲突,才会产生阻塞等待。
synchronized的锁机制
1.锁升级
JVM将synchronized锁分为⽆锁、偏向锁、轻量级锁、重量级锁状态。会根据情况,进⾏依次升
级。
1.偏向锁阶段
核心思想:懒汉模式,能不加锁就不加锁,能晚加锁则晚加锁
偏向锁:并非真正加锁了,而是做了非常轻量的标记
一旦其他线程来和我竞争这个锁,就在另一个线程之前,先把锁获取到
从偏向锁升级到轻量级锁(真正加锁,有互斥)
没有竞争,就把加锁省略
非必要不加锁
在遇到竞争的情况下,偏向锁没有提升效率,但是如果在没有竞争的情况下,偏向锁就大幅度提升效率
2.轻量级锁阶段
有竞争但不多 通过自旋锁方式实现
优:另外的线程把锁释放了,就会第一时间拿到锁
劣:比较耗CPU
与此同时,synchronized内部也会统计 当前这着锁对象,有多少个线程在参与竞争,这里当发生参与竞争的线程比较多了,就会进一步升级到重量级锁
对于自旋锁来说,如果同一个锁竞争者很多,大量的线程都在自旋,整体CPU的消耗就很大
3.重量级锁阶段
此时拿不到锁的线程就不会继续自旋了,而是进行"阻塞等待",就会让出CPU了(不会使CPU占用率太高)
当当前线程释放锁的时候,就由系统随机唤醒一个线程随机唤醒一个线程来获取锁
2.锁消除
也是synchronized 中内置的优化策略
编译器优化中的一种方式,编译器编译代码的时候,如果发现这个代码,不需要加锁,就会自动化把锁干掉
锁消除,针对一眼看上去就完全不涉及线程安全问题的代码,能够把锁消除掉
偏向锁,运行起来才知道有没有锁冲突
3.锁粗化
会把多个细粒度的锁,合并成一个粗粒度的锁
synchronized{} 大括号里包含的代码越少,就认为锁的粒度越细,包含的代码越多,就认为锁的粒度越粗
通常情况下,是更偏好于让锁的粒度细一点,更有利于多个线程并发执行的.但是有的时候,是希望锁的粒度粗点也挺好
总结:
1.锁升级:偏向锁-> 轻量级锁->重量级锁
2.锁消除:自动干掉不必要的锁
3.锁粗话:把多个细粒度的锁合并成一个粗粒度的锁,减少锁竞争的开销