锁和原子操作是怎么实现的

在多线程编程中,对某一资源的同步操作是保证资源状态一致性的关键。这个需要同步的资源可以是单个简单的变量,也可以是多个变量,或者是某些外部资源。对他们同步操作的含义就是同一时间点,最多只能有一个线程在操作这些资源,也就是排他性。并且一系列操作必须一气呵成,中间不允许其他线程做相关的操作,这就是原子性。所以我们在程序中需要让一段代码,在同一时间最多只能有一个线程执行。在Java里我们有Synchronized关键字来标注一段代码在任意时刻只能最多有一个线程在执行。在新的Java Concurrent包里边提供了Lock相关的类。通过Lock我们可以实现与Synchronized同样的效果。这段代码通常我们叫它临界区(Critical Section)。

但是Lock和Synchronize是怎么实现的呢?

从代码上看起来好像在某个地方有个开关。一个线程执行到临界区时,检查这个开关,如果是0代表可以进入,然后把开关设成1,进入,退出,把开关设成0。看起来很完美?其实没有那么简单。因为你这个开关本身可能被多个线程同时检查,它们同时检查到了0,同时进入。。啊 这可不好。再弄个开关?好像陷入无穷无尽的开关也解决不了问题。更天才的想法是,把自己的线程号赋予这个变量,然后等待一小段时间,再检查这个变量是不是自己的进程号,如果是自己,恭喜抢到了。等一小段时间是为了防止其它线程同时检查了,并在你检查之后写了它的线程号。但是,这个等待时间可能很难确定,CPU的线程调度让以上线程间的操作无序且无法知道。看看,这个问题真的貌似不好通过简单的程序诡计解决啊。也许你可以想出更天才的纯软件解决方案,但它可能不那么可靠或者高效。

软件解决不了的问题,其实利用硬件很容易解决。大多CPU都提供了原子操作。请参考开头我提到的文档。CPU保证了某些对单个变量的比较和交换操作是原子的。也就是比较某个数是不是你预期的,如果是,赋予你给的变量,否则不做操作。可以想象CPU在执行此类指令时,可能要暂停多核情况下其它核心的执行,霸占总线,让这个操作不被打断-除非一个CPU周期就可以做完,并且可能要清理CPU的缓存。。。 但是这些还是比我们上边提到的纯软件方法快速且靠谱。

好了,回头看看我们的Java怎么用到CMPXCHG的。

首先进入Java世界。请看ReentrantLock$FairSync.tryAcquire。有没有看到一句话跟我们说的CMPXCHG是同义词?对了,就是compareAndSetState(0, acquires)。一路着下去。。 到了Unsafe.compareAndSwapInt(针对Sun/Oracle Java).这个方法是native的-我们到达了Java世界的边缘。

然后,让我们进入C++的世界。请找到OpenJDK的unsafe.cpp。找到:

UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x))
UnsafeWrapper(“Unsafe_CompareAndSwapInt”);
oop p = JNIHandles::resolve(obj);
jint* addr = (jint *) index_oop_from_field_offset_long(p, offset);
return (jint)(Atomic::cmpxchg(x, addr, e)) == e;
UNSAFE_END

是不是找到了atomic.cpp。你看到了很多平台判断的预处理指令。啊,神奇的预处理,C语言实现跨平台的法宝。看起来到此为止,我们也到达了C/C++世界的边缘。

最后,汇编语言-CPU指令的等价符号。找个比较亲民的平台的把,x86下的atomic_linux_x86.inline.hpp。是的HPP,这里还不是.S。因为C/C++跟汇编的世界交界处不像到Java的世界那样有一堵不透风的墙。汇编可以自由的嵌入C/C++。到此我们终于找到了CPU指令了。

inline jint     Atomic::cmpxchg    (jint     exchange_value, volatile jint*     dest, jint     compare_value) {
int mp = os::is_MP();
__asm__ volatile (LOCK_IF_MP(%4) “cmpxchgl %1,(%3)”
: “=a” (exchange_value)
: “r” (exchange_value), “a” (compare_value), “r” (dest), “r” (mp)
: “cc”, “memory”);
return exchange_value;
}

接下来还有CPU微指令,逻辑电路,这里大概就是软件和硬件世界的边缘了。

时间: 2024-11-04 12:14:56

锁和原子操作是怎么实现的的相关文章

从Java到CPU指令系列之 - 锁和原子操作是怎么实现的 How Lock and atomic works

给急性格的读者: 请参考<Intel 64 and IA32 Architectures Software Develeloper's Manual>.其中有CMPXCHG指令和LOCK指令前缀.或者AMD等其他厂商的开发指南. 在多线程编程中,对某一资源的同步操作是保证资源状态一致性的关键.这个需要同步的资源可以是单个简单的变量,也可以是多个变量,或者是某些外部资源.对他们同步操作的含义就是同一时间点,最多只能有一个线程在操作这些资源,也就是排他性.并且一系列操作必须一气呵成,中间不允许其他

无锁-CAS原子操作

CAS原子操作--Compare & Set,或是 Compare & Swap,现在几乎所有的CPU指令都支持CAS的原子操作,X86下对应的是 CMPXCHG 汇编指令. 大家应该还记得操作系统里面关于"原子操作"的概念,一个操作是原子的(atomic),如果这个操作所处的层(layer)的更高层不能发现其内部实现与结构.原子操作可以是一个步骤,也可以是多个操作步骤,但是其顺序是不可以被打乱,或者切割掉只执行部分.有了这个原子操作这个保证我们就可以实现无锁了. 相对

锁----和 原子操作

互斥锁:只有一个能进入,他完成 下一个才能进去.读写任何地方都要枷锁 读写锁[有大量的读, 时用.读写锁. ]读 多个线程 同事读,不会有影响, 第一个协成 进来 加了读锁,以后的协成 也能进来,但是不能写了.[读的时候不能写. ]写写进入了,读是进不去了.[(写是互斥)] 相同点, 都是 读写全都要加锁: 原文地址:https://www.cnblogs.com/ldms/p/11372913.html

多线程中的锁系统(二)-volatile、Interlocked、ReaderWriterLockSlim

介绍 上章主要说排他锁的直接使用方式.但实际当中全部都用锁又太浪费了,或者排他锁粒度太大了. 这一次我们说说升级锁和原子操作. 目录 1:volatile 2:  Interlocked 3:ReaderWriterLockSlim 4:总结 一:volatile 简单来说: volatile关键字是告诉c#编译器和JIT编译器,不对volatile标记的字段做任何的缓存.确保字段读写都是原子操作,最新值. 这不就是锁吗?   其这货它根本不是锁, 它的原子操作是基于CPU本身的,非阻塞的. 因

多线程中的锁系统(四)-谈谈自旋锁

目录 一:基础 二:自旋锁示例 三:SpinLock 四:继续SpinLock 五:总结 一:基础 内核锁:基于内核对象构造的锁机制,就是通常说的内核构造模式.用户模式构造和内核模式构造 优点:cpu利用最大化.它发现资源被锁住,请求就排队等候.线程切换到别处干活,直到接受到可用信号,线程再切回来继续处理请求. 缺点:托管代码->用户模式代码->内核代码损耗.线程上下文切换损耗. 在锁的时间比较短时,系统频繁忙于休眠.切换,是个很大的性能损耗. 自旋锁:原子操作+自循环.通常说的用户构造模式.

015 原子操作 旋转锁

以下代码个别情况下会出错 导致值为1 #define UNICODE #include <stdio.h> #include <tchar.h> #include <windows.h> #include <process.h> long gNum = 0; unsigned int __stdcall ThreadRun1(void* lParam) { //锁 //原子操作 //同一资源在同一时间只有一个线程能够访问 //系统级的操作 //在硬件里面设置

C#多线程实践——锁和线程安全

锁实现互斥的访问,用于确保在同一时刻只有一个线程可以进入特殊的代码片段,考虑下面的类: class ThreadUnsafe { static int val1, val2; static void Go() { if (val2 != 0) Console.WriteLine (val1 / val2); val2 = 0; } } 这不是线程安全的:如果Go方法被两个线程同时调用,可能会得到在某个线程中除数为零的错误,因为val2可能被一个线程设置为零,而另一个线程刚好执行到if和Conso

IOKING真正无锁服务器引擎之消息引擎模块Demo(no-lock)

 关键词: no-lock interlocked lock-free tcp/ip socket server engine epoll iocp server out-of-orderexecution无锁 原子锁 原子操作 原子指令 锁无关 开放锁 通讯服务器 引擎 高并发 大数据 搜索引擎 完成端口服务器 cpu乱序并行执行 内存栅栏 IOKING 真正无锁服务器引擎之消息引擎模块Demo(no-lock) 这是继无锁iocp通讯模块以后,又一个无锁模块.下一步有时间将会把两个整合在

IOKING MsgEngine无锁消息引擎(no-lock)

 关键词: no-lock interlocked lock-free tcp/ip server engine iocp server out-of-orderexecution无锁 原子锁 原子操作 原子指令 锁无关 开放锁 通讯服务器 引擎 高并发 大数据 搜索引擎 完成端口服务器 cpu乱序并行执行 内存栅栏 Demo下载链接: http://download.csdn.net/detail/guestcode/8579089 这是继无锁iocp通讯模块以后,又一个无锁模块.本来已经