本文转载自微信公众号《代码透视》,作者代码透视。转载本文请联系代码视角公众号。什么是无锁编程LOCK-FREE,字面解释是一种不使用锁来解决多线程、多进程之间的数据同步和访问的程序设计方案。相对来说,是一种通过数据结构和算法来解决数据并发冲突的实现方案。无锁编程“Compare-and-swap”的实现比较和交换,一种解决多线程并行情况下使用锁带来的性能损失的机制,CAS操作包含三个操作数——内存位置(V),预期原值(A)和新值(B)。如果内存位置的值与预期的原始值匹配,处理器会自动用新值更新该位置的值。否则,处理器什么都不做。在任何一种情况下,它都会返回CAS指令之前该位置的值。CAS有效地声明“我认为位置V应该包含值A;如果是,则将B放在这个位置;否则,不要更改那个位置,只需告诉我这个位置现在是什么。”(百度百科)参考图使用场景(1)乐观锁实现方案:无锁,假设没有冲突完成一个操作,如果因为冲突导致失败,则重试,直到成功。缺点(1)循环开销问题。长期修改不成功,会消耗大量CPU。解决方案:修改失败后需要执行其他逻辑,CAS不适合大量资源竞争。(2)ABA问题:线程1准备使用CAS将变量的值从A替换为B,在此之前,线程2将变量的值从A替换为C,又将变量的值从C替换为A。然后线程1在线程1执行CAS的时候发现变量的值还是A,所以CAS成功了。但实际上此时的站点已经和原来的不一样了。“DataHash”数据Hash实际上是通过Hash算法预先确定由哪个节点来处理或存储数据。解决数据并发的思路是通过算法将不同的数据解析到不同的节点上。算法:data.hashCode()%节点数。参考图使用定时任务处理数据时的场景(1)。例如:一个定时任务数据量大,需要集群处理。那么就可以同时启动读取数据的任务,然后根据idHash来决定当前节点是否应该处理这条数据。(2)请求到指定的服务器进行处理。比如:Nginx的ipHash转发策略,Kafka的hash分区保证分区顺序。缺点(1)扩容相对复杂,需要数据迁移。比如一致性哈希算法,Kafka分区重平衡策略。但有些场景并不一定支持扩容。(2)哈希算法是否哈希。如果算法不够哈希,就会出现数据倾斜。“单线程”在某些场景下,单线程设计比多线程更好。单线程下没有资源竞争和线程切换,当然这也取决于你现在的服务器配置。例如:(1)在Redis的设计中,由于是内存级别的K/V数据库,频繁的CPU切换、线程等待唤醒、获取锁资源等都会在处理核心读写时造成性能瓶颈。(2)在生成分布式id的场景下,此时一个idserver可以批量生成id,也可以单线程处理,内存计算非常高效。什么时候使用单线程?(1)单核服务器。(2)在CPU计算量大,数据冲突多的业务场景下(不是绝对的)。无锁编程的优缺点“优点”是没有优先级倒置。不会出现死锁、饿死、饿死等问题。减少资源竞争,消耗更少的CPU资源,更高效。“劣势”具有一定的复杂性,需要一定的算法思维。不适合所有场景,非全局最优解。小结在设计程序时,应该考虑程序的使用场景,设计出最优的数据结构和算法。无锁编程只是针对特定场景的一种解决方案,并不一定代表最优方案。结论优秀的设计模式与优秀的数据结构相结合,才能产生优秀的代码。程序员的内功:数据结构+算法。
