Java多线程(九)

目录

一、锁策略

1.1 乐观锁 vs 悲观锁

1.2 轻量级锁 vs 重量级锁

1.3 自旋锁 vs 挂起等待锁

1.4 读写锁 vs 互斥锁

1.5 公平锁 vs 非公平锁

1.6 可重入锁 vs 不可重入锁


一、锁策略

1.1 乐观锁 vs 悲观锁

这里的乐观锁和悲观锁不是指的某一个具体的锁,而是指的“一类锁”

乐观锁:预测锁竞争不是很激烈(这里做的工作相对较少)。一开始的时候是不会加锁的,会引入一个版本号,借助版本号识别出当前的数据访问时候冲突

悲观锁:预测锁竞争是很激烈的(这里做的工作比较多)。一开始就会去加锁,会在每次访问共享变量之前去真正加锁。

区别:对锁竞争预测的激烈程度,以及后续工作的多少

1.2 轻量级锁 vs 重量级锁

轻量级锁:加锁机制尽可能不使用 mutex,而是尽量在用户态代码完成。实在搞不定了,再使用 mutex。少量的内核态用户态切换,不太容易引发线程调度。加锁的开销比较小(花的时间少,占用系统资源比较少,效率比较高)

重量级锁:加锁机制重度依赖了 OS 提供了 mutex,大量的内核态用户态切换很容易引发线程的调度。加锁的开销比较大(花的时间多,占用系统资源比较多,效率比较低)

1.3 自旋锁 vs 挂起等待锁

自旋锁:轻量级锁的一种典型实现。如果获取锁失败,立即再尝试获取锁,无限循环,直到获取到锁为止。第一次获取锁失败,第二次的尝试会在极短的时间内到来。一旦锁被其他线程释放,就能第一时间获取到锁。

  • 优点: 没有放弃 CPU,不涉及线程阻塞和调度,一旦锁被释放,就能第一时间获取到锁。
  • 缺点: 如果锁被其他线程持有的时间比较久,那么就会持续的消耗 CPU 资源。(而挂起等待的时候是不消耗 CPU 的)。

挂起等待锁:重量级锁的一种典型实现。通过内核态,借助系统提供的锁机制,当出现锁冲突的时候,会牵扯到内核对于线程的调度,使冲突的线程出现挂起等待(阻塞等待)

  • 优点:在挂起等待的过程中,不会去消耗cpu资源
  • 缺点:在等待的过程中,上一个线程释放了锁需要一定的时间才能获取到,无法保证第一时间获取到锁。

1.4 读写锁 vs 互斥锁

读写锁:多线程之间,数据的读取方之间不会产生线程安全问题,但数据的写入方互相之间以及和读者之间都需要进行互斥。如果两种场景下都用同一个锁,就会产生极大的性能损耗。所以这里就产生了读写锁

  • 读加锁和读加锁之间,不互斥。(只读不会产生多线程安全问题,所以在读操作的过程中不加锁,会提高效率)
  • 写加锁和写加锁之间,互斥。
  • 读加锁和写加锁之间,互斥。

互斥锁:就是synchronized这样的锁,提供了加锁解锁两个操作,如果一个线程加锁了,另一个线程也尝试加锁,就会阻塞等待

1.5 公平锁 vs 非公平锁

公平锁:遵循“先来后到”,如果A获取了锁B与C都在等待且B排在C的前面,那么当A释放锁的时候,就应该是B先拿到锁。

非公平锁:不遵循“先来后到”,释放锁的时候谁抢到就是谁的

注意:这里的公平和非公平并没有好坏之分,只是取决于不同的应用场景!!!

1.6 可重入锁 vs 不可重入锁

可重入锁:针对一把锁重复加锁不会产生死锁

不可重入锁:针对一把锁重复加锁会产生死锁

猜你喜欢

转载自blog.csdn.net/x2656271356/article/details/132239192