深入了解锁细节

简介: 深入了解锁细节

背景

编写并行程序必须要面对的一个难题:如何正确有效的保护共享数据。一些场景必然无法逃避锁的使用,最常见的应用就是:本地缓存。当使用锁的时候我们该注意什么?当锁成为性能瓶颈的时候我们该怎么优化做到有的放矢?

操作代价

如果关注性能,自然少不了要理解下,锁使用过程中性能到底消耗在哪里。如果一个锁自始至终只被一个线程使用。或者,一个锁被多个线程使用过,但是在任意时刻,都只有一个线程尝试获取锁,我们将以上两种锁称为非竞争锁,这种情况对性能的影响很小。对性能影响最严重的情况出现在多个线程同时尝试获取锁,即锁竞争。性能影响主要在于上下文切换以及数据的cacheline miss。因此,面对需要使用锁的情况,不要过分担心非竞争锁带来的开销,要关注那些真正发生了锁竞争的临界区中性能的优化。

加锁耗时:10ns

原子+1:50ns

CAS:100ns

自旋锁竞争(cacheline miss):100ns

信号量竞争(上下文切换):1000ns

内存访问:100ns

http://yarchive.net/comp/linux/lock_costs.html

因此,在保证程序正确性的前提下,解决锁同步带来的性能损失的主要方向应该是降低锁竞争。通常,有以下三类方法:

  1. 减少持有锁的时间
  2. 降低请求锁的频率
  3. 用其他协调机制取代独占锁

减少持有锁的时间

尽可能缩短持有锁的时间。这可以通过把不需要用锁保护的代码移出同步块来实现, 尤其是那些花费“昂贵”的操作,以及那些潜在的阻塞操作

降低请求锁的频率

  • 分拆锁
    如果一个锁守护多个相互独立的状态变量,你可能能够通过分拆锁,使每一个锁守护不同的变量,使每一个锁被请求的频率都变小了。分拆锁对于中等竞争强度的锁,能够有效地把它们大部分转化为非竞争的锁
  • 分离锁
    将大对象分成若干加锁块的集合,并且它们归属于相互独立的对象。例如,ConcurrentHashMap 的实现使用了一个包含 16 个锁的数组,每一个锁都守护 HashMap 的 1/16 。假设 Hash 值均匀分布,将会把对于锁的请求减少到约为原来的 1/16 。当多处理器系统的大负荷访问需要更好的并发性时,锁的数量还可以增加。
  • 避免热点域
    在某些应用中,我们会使用一个共享变量缓存常用的计算结果。每次更新操作都需要修改该共享变量以保证其有效性。例如,队列的 size,counter,链表的头节点引用等。在多线程应用
    中,该共享变量需要用锁保护起来。这种在单线程应用中常用的优化方法会成为多线程应用中的“热点域 (hot field) ”,从而限制可伸缩性。如果一个队列被设计成为在多线程访问时保持高
    吞吐量,那么可以考虑在每个入队和出队操作时不更新队列 size 。 ConcurrentHashMap 中为了避免这个问题,在每个分片的数组中维护一个独立的计数器,使用分离的锁保护,而不是维护一个全局计数。
  • 替代独占锁
    使用更高效的并发方式管理共享状态。例如并发容器,读-写锁,不可变对象,以及原子变量。
  • 多个读者可以并发访问共享资源,但是写者必须独占获得锁。对于多数操作都为读操作的数据结构,读写锁比独占锁提供更好的并发性。
  • 原子变量提供了避免“热点域”更新导致锁竞争的方法,如计数器、序列发生器、或者对链表数据结构头节点引用的更新。

总结

以上只是一些通用的减少锁竞争的手段,针对不同的业务场景还可以使用其他措施来优化锁竞争,例如:使用自旋锁代替独占锁,批量读取数据,减少锁冲突导致的上下文切换。

本文作者 : cyningsun

本文地址https://www.cyningsun.com/04-24-2018/lock.html

版权声明 :本博客所有文章除特别声明外,均采用 CC BY-NC-ND 3.0 CN 许可协议。转载请注明出处!

目录
相关文章
|
5月前
|
Python
Python多线程中递归锁如何解决死锁问题的详细阐述
Python多线程中递归锁如何解决死锁问题的详细阐述
|
6月前
|
安全 算法 关系型数据库
线程安全--深入探究线程等待机制和死锁问题
线程安全--深入探究线程等待机制和死锁问题
179 1
|
安全 算法 Java
可重入锁,不可重入锁,死锁的多种情况,以及产生的原因,如何解决,synchronized采用的锁策略(渣女圣经)自适应的底层,锁清除,锁粗化,CAS的部分应用
可重入锁,不可重入锁,死锁的多种情况,以及产生的原因,如何解决,synchronized采用的锁策略(渣女圣经)自适应的底层,锁清除,锁粗化,CAS的部分应用
|
6月前
|
Java
【专栏】Java多线程中,锁用于控制共享资源访问,确保数据一致性和正确性,锁是什么意思,有哪些分类?
【4月更文挑战第28天】Java多线程中,锁用于控制共享资源访问,确保数据一致性和正确性。本文探讨锁的概念、作用及分类:乐观锁与悲观锁、自旋锁与适应性自旋锁、公平锁与非公平锁、可重入锁和读写锁。使用锁需注意避免死锁、合理选择锁粒度及性能优化。理解锁有助于提升多线程编程的效率和稳定性。
104 0
死锁概念
本章讲解什么是死锁以及如何解决
64 0
|
Java 编译器 调度
锁的优化过程
锁的优化过程
|
存储 安全 Java
大白话讲解synchronized锁升级套路
synchronized锁是啥?锁其实就是一个对象,随便哪一个都可以,Java中所有的对象都是锁,换句话说,Java中所有对象都可以成为锁。 这次我们主要聊的是synchronized锁升级的套路
126 0
|
关系型数据库 中间件 MySQL
上手全局锁,死锁
上手全局锁,死锁
上手全局锁,死锁
|
存储 安全 Java
|
存储 Java 对象存储