锁的概念及整理(简单理解)
公平锁与非公平锁
公平锁:
- 特点:多个线程在等待同一把锁,等待时间最长的将获得锁
- 优点:所有的线程都能获得资源,不会饿死在队列中
- 缺点:吞吐量下降,除了队列中的第一个线程,其余线程都会被阻塞,cpu唤醒线程的开销较大
非公平锁:
- 特点:如果有多个线程请求同一个锁,那个该锁将随机分配给其中的一个线程
- 优点:减少 CPU 唤醒线程的开销,吞吐量有所提高
- 缺点:存在线程一直无法获取到锁的可能性,导致线程饿死
乐观锁与悲观锁
乐观锁:
- 特点: 总是假设最糟糕的情况,认为每次读取数据时都会被修改,为了避免脏读的情况出现,在每次读取数据时都会加上锁
- 优点:能够有效的避免脏读的情况
- 缺点:效率低下
- 示例: synchronized 关键字 和 JUC 的 lock
悲观锁:
- 特点:总是假设最好的情况,认为每次读取数据时都不会被修改,所以不会上锁,但在更新数据时会判断数据是否被修改
- 优点:便于提高吞吐量
- 示例:JUC 的原子变量(CAS)
重入锁与不可重入锁
可重入锁:
- 特点: 在当前代码段中, 已获取对象 A 的锁,但是在改代码段中,掉用的另一个函数也需要获取对象 A 的锁,那么为了保证线程不被阻塞,函数能够顺利获得该对象的锁,那么这样的锁便是可重入锁。反之则称为不可重入锁。
- 示例: 例如 Reentaran* 的类,均是可重入的锁
【示例】
public synchronized void f1(){
// code...
f2()
// code...
}
public synchronized void f2(){
}
假设在同一个对象中,有两个函数 f1()
,f2()
,在 f1
中调用 f2
,此时 f1
已获得锁,如果 f2
未能获得锁,该进程便会被阻塞,即 f2
无法获得 f1
的锁,这样的锁便被称为不可重入锁。为了保证进程顺利运行,那么就需要 f2
能够获得 f1
得到的锁,这样重复得到的锁被称之为可重入锁
公平锁与非公平锁
公平锁
- 概念: 当发生线程之间对于锁的竞争时,如果线程按照进入等待队列的先后次序获得锁,这样按照先进先出规则的策略就被称之为公平锁。
非公平锁
- 概念: 当线程竞争锁时,线程不用按照先来后到的顺序,谁先抢到锁,那个锁就是谁的,这样的锁便被称为非公平锁
自旋锁
自旋锁:
- 特点:当一个线程拿不到锁时,并不会放弃 CPU ,而是通过循环去不停的尝试去获取锁,这就是所谓的自旋锁
- 示例: JUC 中 Atomic 类,皆是带有自选性质的锁
偏向锁
偏向锁
- 特点:偏向锁的意思是锁会偏向于第一个获得它的线程,当锁对象第一次被其他线程获取时,虚拟机会将线程的 id 记录在对象的 MarkWord 中,如果 CAS 操作执行成功,则持有偏性锁的线程每次进入相同的同步块时,虚拟机都不需要再进行任何同步操作(加锁、解锁、MarkWord的更新等)只要在执行过程中,该锁对象一值没有被其他对象获取,则持有偏向锁的对象不需要同步。
轻量级锁和重量级锁
轻量级锁
轻量级锁其设计目的是在没有线程竞争情况下,单一线程进入同步代码块时,在没有其他线程进行竞争的情况下,减少当前线程重复进行同步操作所带来的消耗。偏向锁就是一种轻量级锁。
重量级锁
重量级锁指的是使用操作系统互斥量来实现的传统锁