好久没有更新博客了,这两个月发生的几件事情耽误了很多计划,现在总算慢慢步入正轨…今天写写平时在并发编程中使用锁的实现原理。本文以Java和Go语言为例。

Java和Go语言中的锁都是基于CAS实现,CAS就是比较和交换(Compare And Set)这是一个原子过程,需要硬件层面提供支持,在汇编上涉及核锁。

Java中java.util.concurrent.locks包中的锁的实现方式都是基于CAS。CAS的相关操作由操作系统提供相关的接口,在Java中,它们封装在java.util.concurrent.atomic上。

下面实现一个简单的锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.util.concurrent.atomic.AtomicInteger;

public class SimpleLock {
private AtomicInteger status = new AtomicInteger(0);

public void lock() {
while(!status.compareAndSet(0, 1)) {
return
}
}

public void unlock() {
status.compareAndSet(1, 0);
}
}

代码中,0表示锁没有被获取,1表示锁已经被一个线程占有。获取锁时,会原子地把status从0设置为1,然后返回,让线程继续执行其代码,如果status已经为1,那么获取锁的线程一直循环地尝试获取锁,而不是加入锁等待队列。

这个示例反映了锁的最核心实现,它还需要很多改进。比如,解决ABA问题,读写分离的锁,饥饿问题。

下面给出Go语言的实现,原理上一样,语法层面也相似。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package main

import (
"sync/atomic"
)

type Mutex struct {
state int32
}

func (m *Mutex) Lock() {
for(!atomic.CompareAndSwapInt32(&m.state, 0, 1)) {
return
}
}

func (m *Mutex) Unlock() {
atomic.CompareAndSwapInt32(&m.state, 1, 0)
}

完善锁的功能,包括饥饿处理,超时控制,spin,线程等待队列,公平性…这样我们就有了一把基于CAS完整的锁了。

以上。