无锁编程的原理和应用
一、基于锁的编程的缺点
在传统的多线程编程中,多线程之间一般用各种锁的机制来保证正确的对共享资源进行访问和操作。只要需要共享某些数据,就应当将对它的访问串行化。比如像++count(count是整型变量)这样的简单操作也得加锁,因为即便是增量操作这样的操作,实际上也是分三步进行的:读、改、写(回)。
在使用锁机制的过程中,即便在锁的粒度、负载、竞争、死锁等需要重点控制的方面解决的很好,也无法彻底避免这种机制的如下一些缺点:
1. 锁机制会引起线程的阻塞,对于没有能占用到锁的线程或者进程,将一直等待到锁的占有者释放锁资源后才能继续执行,而等待时间理论上是不可设置和预估的。
2. 申请和释放锁的操作,增加了很多访问共享资源的消耗,尤其是在锁竞争很严重的时候。
3. 现有实现的各种锁机制,都不能很好的避免编程开发者设计实现的程序出现死锁或者活锁的可能。
4. 难以调试。
二、无锁编程的定义
无锁编程按字面最直观的理解是不使用锁的情况下实现多线程之间对变量同步和访问的一种程序设计实现方案。一个锁无关的程序能够确保它所有线程中至少有一个能够继续往下执行,而有些线程可能会被的延迟。然而在整体上,在某个时刻至少有一个线程能够执行下去。作为整体进程总是在前进的,尽管有些线程的进度可能没有其它线程进行的快。
三、无锁编程的原理
无锁编程具体使用技术方法包括:原子操作(atomic operations), 内存栅栏(memory barriers), 内存顺序冲突(memory order), 指令序列一致性(sequential consistency)和顺ABA现象等等。其中最重要的是原子操作,其中最常用的原子操作又是CAS(COMPARE AND SWAP)算法。
CAS算法的过程是这样:它包含3个参数CAS(V,E,N)。V表示要更新的变量,E表示预期值,N表示新值。仅当V值等于E值时,才会将V的值设为N,如果V值和E值不同,则说明已经有其他线程做了更新,则当前线程什么都不做。最后,CAS返回当前V的真实值。CAS操作是抱着乐观的态度进行的,它总是认为自己可以成功完成操作。当多个线程同时使用CAS操作一个变量时,只有一个会胜出,并成功更新,其余均会失败。失败的线程不会被挂起,仅是被告知失败,并且允许再次尝试,当然也允许失败的线程放弃操作。
在java语言中在java.util.concurrent包中大量实现都是建立在基于CAS实现Lock-Free算法上。java.util.concurrent.atomic提供了基于CAS实现的若干类:
- AtomicBoolean – 原子布尔
- AtomicInteger – 原子整型
- AtomicIntegerArray – 原子整型数组
- AtomicLong – 原子长整型
- AtomicLongArray – 原子长整型数组
- AtomicReference – 原子引用
- AtomicReferenceArray – 原子引用数组
- AtomicMarkableReference – 原子标记引用
- AtomicStampedReference – 原子时间戳记引用(解决ABA现象)
- AtomicIntegerFieldUpdater – 用来包裹对整形 volatile 域的原子操作
- AtomicLongFieldUpdater – 用来包裹对长整型 volatile 域的原子操作
- AtomicReferenceFieldUpdater – 用来包裹对对象 volatile 域的原子操作
引入这些类的主要目的就是为了实现Lock-Free算法和相关数据结构,以incrementAndGet这个方法为例:
public final int incrementAndGet() {for (;;) {int current = get();int next = current + 1;if (compareAndSet(current, next))return next;}
}
在这里使用CAS原子操作,每次读取数据数值后将此数值和+1后的结果进行CAS操作,如果成功就返回结果,否则重试到成功为止。其中compareAndSet是java中实现的CAS函数,在java语言中的实现,是借助JNI机制来调用汇编实现的。
public final boolean compareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
unsafe.compareAndSwapInt是个本地方法调用,对应的x86处理器的jni源码。
四、无锁编程(同步)栈和队列的实现
1、无锁栈
package concurrenttest; import java.util.concurrent.atomic.AtomicReference; public class ConcurrentStack<E> { AtomicReference<Node<E>> top = new AtomicReference<Node<E>>(); public void push(E item) { Node<E> newHead = new Node<E>(item); Node<E> oldHead; while (true) { oldHead = top.get(); newHead.next = oldHead; if (top.compareAndSet(oldHead, newHead)) { return; } } } public E pop() { while (true) { Node<E> oldHead = top.get(); if (oldHead == null) { return null; } Node<E> newHead = oldHead.next; if (top.compareAndSet(oldHead, newHead)) { return oldHead.item; } } } private static class Node<E> { public final E item; public Node<E> next; public Node(E item) { this.item = item; } }
}
2、无锁队列(ConcurrentLinkedQueue)
public boolean offer(E e) { checkNotNull(e); final Node<E> newNode = new Node<E>(e); for (Node<E> t = tail, p = t;;) { Node<E> q = p.next; if (q == null) { // p is last node if (p.casNext(null, newNode)) { // Successful CAS is the linearization point // for e to become an element of this queue, // and for newNode to become "live". if (p != t) // hop two nodes at a time casTail(t, newNode); // Failure is OK. return true; } // Lost CAS race to another thread; re-read next } else if (p == q) // We have fallen off list. If tail is unchanged, it // will also be off-list, in which case we need to // jump to head, from which all live nodes are always // reachable. Else the new tail is a better bet. p = (t != (t = tail)) ? t : head; else // Check for tail updates after two hops. p = (p != t && t != (t = tail)) ? t : q; }
}
五、无锁编程的注意事项
循环CAS操作对时会大量占用cpu,对系统时间的开销也是很大。这也是基于循环CAS实现的各种自旋锁不适合做操作和等待时间太长的并发操作的原因。
无锁编程的原理和应用相关推荐
- 浅谈Linux内核无锁编程原理
非阻塞型同步 (Non-blocking Synchronization) 简介 如何正确有效的保护共享数据是编写并行程序必须面临的一个难题,通常的手段就是同步.同步可分为阻塞型同步(Blocking ...
- 【翻译】RUST无锁编程
本文内容译自Lock-freedom without garbage collection,中间有少量自己的修改. 人们普遍认为,垃圾收集的一个优点是易于构建高性能的无锁数据结构.对这些数据结构进行手 ...
- 无锁编程(Lock Free)框架 系列文章
无锁编程(Lock Free)框架 系列文章: 1 前置知识:伪共享 原理 & 实战 2 disruptor 使用和原理 图解 3 akka 使用和原理 图解 4 camel 使用和 原理 图 ...
- 交易系统开发技能及面试之无锁编程(Lock-free)
目录 概要 Q1 什么是atomic? Q2 alignas 关键字 Q3 C++的内存模型 relaxed ordering, release-acquire ordering, sequentia ...
- 【C++】多线程与原子操作和无锁编程【五】
[C++]多线程与原子操作和无锁编程[五] 1.何为原子操作 前面介绍了多线程间是通过互斥锁与条件变量来保证共享数据的同步的,互斥锁主要是针对过程加锁来实现对共享资源的排他性访问.很多时候,对共享资源 ...
- 我是如何一步步的在并行编程中将lock锁次数降到最低实现无锁编程
在并行编程中,经常会遇到多线程间操作共享集合的问题,很多时候大家都很难逃避这个问题做到一种无锁编程状态,你也知道一旦给共享集合套上lock之后,并发和伸缩能力往往会造成很大影响,这篇就来谈谈如何尽可能 ...
- 帖子如何实现显示浏览次数_我是如何一步步的在并行编程中将lock锁次数降到最低实现无锁编程...
在并行编程中,经常会遇到多线程间操作共享集合的问题,很多时候大家都很难逃避这个问题做到一种无锁编程状态,你也知道一旦给共享集合套上lock之后,并发和伸缩能力往往会造成很大影响,这篇就来谈谈如何尽可能 ...
- 原理剖析(第 012 篇)Netty之无锁队列MpscUnboundedArrayQueue原理分析
原理剖析(第 012 篇)Netty之无锁队列MpscUnboundedArrayQueue原理分析 - 一.大致介绍 1.了解过netty原理的童鞋,其实应该知道工作线程组的每个子线程都维护了一个任 ...
- ZMQ无锁队列的原理与实现
ZMQ无锁队列的原理与实现 前言 1. 为什么需要⽆锁队列 2. 无锁队列的实现(参考zmq,只支持一写一读的场景) 2.1 无锁队列前言 2.2 原⼦操作函数介绍 2.3 yqueue_t的chun ...
- 无锁编程与有锁编程的效率总结、无锁队列的实现(c语言)
1.无锁编程与有锁编程的效率 无锁编程,即通过CAS原子操作去控制线程的同步.如果你还不知道什么使CAS原子操作,建议先去查看相关资料,这一方面的资料网络上有很多. CAS实现的是硬件级的互斥,在线程 ...
最新文章
- HTML有几种发音模式,html – 使用lang属性和拼音发音的正确方法是什么(如果有的话)?...
- php语+言教程,【杂谈】php言语入门教程(PHP编程进修路线图)
- 领地柜怎么砸_5㎡餐厅也配有餐边柜!布局、尺寸都给你们准备好,照着装准没错...
- 深入剖析.NETCORE中CORS(跨站资源共享)
- azm335x 串口配置
- CNN中的卷积操作与权值共享
- 成功自我暗示三大规律
- springboot中得注解_SpringBoot 中的基本注解
- 解决win10可以上网但出现小地球的情况
- Android应用程序开发(第三版)-课后习题解答
- [生存志] 第142节 韩赵陈郑燕辽
- 中国智慧园区标准化白皮书 附下载
- 微信获取access_token 返回-1000
- FTP 打开文件夹提示该文件没有程序与之关联来执行该操作 请在控制面板的文件夹选项中创建关联
- phalcon蹩脚的面包屑(breadcrumbs)功能
- 深聊性能测试,从入门到放弃之:如何对IO进行性能调优
- 3D Models (3D 模型)
- 北京东物流,南顺丰速运
- pagerank 的介绍
- 基于C语言的智能门禁系统,基于RFID技术的智能门禁系统设计
热门文章
- 破解XP 管理员Administrator密码
- 计算机科学与技术 未来,浅析计算机科学与技术的未来发展趋势
- Unity3D 虚拟现实开发(一)
- 更改我的网页默认的暴风影音播放器
- 【硬件和驱动】如何查看linux的驱动有没有装好 ——声卡无声音,有驱动为例子,网卡wifi驱动相关
- c#html表格样式大全,table完美css样式,table的基本样式,table样式
- 使用layui修改table样式
- ElementUI table 样式修改
- 简单工厂模式(Simple Factory Pattern)【1/23】
- Qt —— 完美制作win系统扬声器、麦克风控制程序