自己实现一个锁我们要准备一个变量、一个队列、Unsafe类来执行CAS/park/unpark的Unsafe类。
? 一个变量的值为1的时候就说明已加锁,变量值为0的时候就说明未加锁
? 多个线程对同一个锁的争用肯定只有一个能成功,没有抢到锁的其它的线程就要排队,所以我们还需要一个队列
Unfafe类
? 多个线程只能有一个线程把变量的值修改为1,且当它的值为1的时候其它线程不能再修改它的值,我们使用Unsafe这个类来做CAS操作。而且排队的线程不能继续往下执行,只能阻塞。我们用Unsfae类来阻塞(park)和唤醒(unpark)线程。
大体的流程示意图如下:
主要代码
public class MyLock {
//用来标记当前锁是否被线程占用,0:不占用 1:不占用
private volatile int state;
private Node empty = new Node();
// 队列头
private volatile Node head;
// 队列尾
private volatile Node tail;
public MyLock() {
head = tail = empty;
}
private static Unsafe unsafe;
// state的偏移量
private static long stateOffset;
// tail的偏移量
private static long tailOffset;
static {
try {
// 获取Unsafe的实例
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
unsafe = (Unsafe) f.get(null);
// 获取state的偏移量
stateOffset = unsafe.objectFieldOffset(MyLock.class.getDeclaredField("state"));
// 获取tail的偏移量
tailOffset = unsafe.objectFieldOffset(MyLock.class.getDeclaredField("tail"));
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
// 原子更新state字段
private boolean compareAndSetState(int expect, int update) {
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
// 原子更新tail字段
private boolean compareAndSetTail(Node expect, Node update) {
return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
}
// 加锁
public void lock() {
// 尝试更新state字段,更新成功说明占有了锁
if (compareAndSetState(0, 1)) {
return;
}
// 未更新成功入队
Node node = enq();
Node prev = node.prev;
// 入队之后,再次尝试获取锁
// 需要检测当前入队节点的上一个节点是不是head,不是则阻塞。
while (node.prev != head || !compareAndSetState(0, 1)) {
// 未获取到锁,阻塞
unsafe.park(false, 0L);
}
// 下面不需要原子更新,因为同时只有一个线程访问到这里
// 获取到锁了且上一个节点是head
// head后移一位
head = node;
// 清空当前节点的内容,协助GC回收
node.thread = null;
// 将上一个节点从链表中剔除,协助GC回收
node.prev = null;
prev.next = null;
}
// 解锁
public void unLock() {
// 把state更新成0,这里不需要原子更新,因为同时只有一个线程访问到这里
state = 0;
// 下一个待唤醒的节点
Node next = head.next;
// 下一个节点不为空,就唤醒它
if (next != null) {
unsafe.unpark(next.thread);
}
}
/**
* 没有抢到锁的线程入队列(入队过程是原子的)
*/
private Node enq() {
while (true) {
Node t = tail;
Node node = new Node(Thread.currentThread(), t);
if (compareAndSetTail(t, node)) {
node.prev = t;
t.next = node;
return node;
}
}
}
}
测试类
public class TestMyLock {
private static int count = 0;
@Test
public void testMyLock() throws InterruptedException {
MyLock lock = new MyLock();
CountDownLatch countDownLatch = new CountDownLatch(1000);
IntStream.range(0, 1000).forEach(i -> new Thread(() -> {
lock.lock();
try {
IntStream.range(0, 10000).forEach(j -> {
count++;
});
} finally {
lock.unLock();
}
countDownLatch.countDown();
}, "tt" + i).start());
countDownLatch.await();
System.out.println(count);
}
}
测试结果
未加锁打印:
测试加锁后,是我们要的结果,锁有效
假设现在有3个线程进入lock方法
? 线程1原子更新state成功然后去执行下面的逻辑。线程2和线程3没有抢到锁,都阻塞在unsafe.park(false, 0L)处。并且此时队列是这样的:Node(empty)—Node(线程2)—Node(线程3)
? 线程1执行业务代码结束并执行unLock方法,执行unLock方法后,在unsafe.park(false, 0L)处阻塞的线程
? 线程2在线程1执行unLock方法的unsafe.unpark(next.thread)被唤醒,再执行一次while (node.prev != head || !compareAndSetState(0, 1)),发现比对符合条件,执行下面的代码。并且队列现在变成Node(线程2)—Node(线程3)
接下来就是重复上面的调用过程。
原文:https://www.cnblogs.com/gaoweiBlog/p/14888295.html