特性
- 是否锁定资源:悲观锁、乐观锁
- 多个线程是否可以共享锁:共享锁,排他锁
- 同一个线程是否可以重复获取锁:可重入锁,不可重入锁
- 多个线程竞争:公平锁、非公平锁(先插队,插队失败在排队)
逻辑推导
先有一个“普通锁”。
- 乐观锁:
- 悲观锁在并发量不高的情况下,每次加锁都要消耗性能。所以可以不加锁,而使用其他方式判断资源是否被改变过。如version,CAS
- 但是在并发量高的情况下,就会造成反复修改,效率下降。
- 共享/排他:对锁,资源,操作细分,提高性能。
- 读读共享:ReentrantReadWriteLock
- 读写共享:StampedLock
- 重入:一个线程重复加锁,可以直接获得锁。如果不可重入,则一个线程只能加一次锁,不可多次加锁,稍不注意则会死锁
- 公平:如果加锁线程可以直接锁,而不排队,不用切换线程提升性能,但可能会造成排队的线程饿死。
- 自旋:使用如CAS等乐观锁的方式不断尝试获取锁,
- 线程不阻塞,但是会一直空跑(尝试获取锁)
- 可以免去切换线程上下文的时间,若是同步的资源(操作)很少,很快获得锁,则效率提升。否则,效率下降
- 自适应自旋:可以通过一些方式决定自旋的次数或时间,平衡阻塞和自旋的效率。例如synchronized的锁膨胀过程
java中的锁
synchronized关键字
特性
- 可重入
- 配合wait,notify实现线程间的等待和唤醒
语法与使用
抛开使用细节,synchronized总的来说只能对实例加锁,通过对实例的Class对象加锁 实现了对类的加锁。
- 修饰静态方法,即是对当前类加锁
- 修饰实例方法,即是对当前对象加锁
- 修饰代码块,指定锁定的对象,常见的锁定对象如:
- 本类的Class对象
- this(本实例)
- 本类中静态的“锁对象”
- 本实例中的“锁对象”
JVM实现原理
- 详情:https://editor.csdn.net/md/?articleId=115748322
- 锁膨胀->无锁,偏向锁,轻量级锁,重量级锁
Lock接口
ReentrantLock:可重入锁
ReentrantReadWriteLock:可重入读写锁,读读不互斥
StampedLock:可重入读写锁,读写不互斥
Condition接口,配置Lock接口实现条件唤醒。
- 一个类,一把锁,多个条件。实现对方法的精准唤醒