关于 CPU 锁和应用锁
- 关于应用层的锁和CPU的锁的关系
(1)Cpu锁
CPU锁主要解决的是多个核心并发访问/修改同一块内存的问题。所以有锁总线和MESI协议来做。对于上层主要的抽象就是CAS,主要的招数就是用CAS+循环来抢东西。
1)如果抢不到就只能继续循环下去玩命抢(这时会空耗CPU)
2)不抢了,回复给上层代码“抢不到”。
(2)应用层的锁
应用层的锁存在了“进程/线程“的概念,解决的是多个进程并发访问同一块内存的问题。比起CPU的层级来说应用层的锁可以多一个招数,叫做“让给让前进程不可调度“。这个是OS提供的支持。因此在应用层的层次上你可以定义一个高级的“锁”,大概执行这样一个抢锁流程
1)尝试用CAS抢到锁
2)如果抢不到,则回到1重试
3)如果抢了几十次都还抢不到,就把当前进程(的信息)尝试挂到一个等待队列上(当然挂的过程还是要CAS)
4)把当前进程设定为不可调度,这样OS就不会把当前进程调度给CPU执行。这种情况因为需要做一次系统调用,所以有比较大的损耗,一般被称为“重量级锁”
而当某个进程释放锁时,他就可以做释放锁的流程
1)找到释放锁的那个等待队列
2)把等待队列里第一个等待的进程信息取出来,并且告诉OS,这个进程程可以执行了(这里也要做一次系统调用)
3)这个被复活了的进程一般需要在做一次循环尝试抢锁,然后就回到了上面的抢锁流程。 - AtomicInteger源码分析
下面模拟不是CAS的真正实现,其实我们在语言层面是没有做任何同步的操作的,大家也可以看到源码没有任何锁加在上面。这就是Atomic包下这些类的奥秘:语言层面不做处理,我们将其交给硬件—CPU和内存,利用CPU的多处理能力实现硬件层面的阻塞,再加上volatile变量的特性即可实现基于原子操作的线程安全。所以说,CAS并不是无阻塞,只是阻塞并非在语言、线程方面,而是在硬件层面,所以无疑这样的操作会更快更高效!
public final int getAndIncrement() {
for (;😉 {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return current;
}
}
无锁在java里面一般就是cas和spin来实现的
Java CAS 原理分析:https://segmentfault.com/a/1190000014858404
JDK 1.8 sun.misc.Unsafe类CAS底层实现:https://www.cnblogs.com/snowater/p/8303698.html
JAVA中的CAS, lock前缀一致性协议保证操作的原子性: https://blog.csdn.net/mmoren/article/details/79185862