建議學習前,做好如下準備
1、閱讀Unsafe源碼
2、CAS原理
代碼
import sun.misc.Unsafe;
import java.lang.reflect.Field;
/**
* @Author 李雷(KyLin)
* @Desc
* @Date 2019/12/31
*/
public class Sync {
static final Node EMPTY = new Node();
private volatile int state;//0空閒,1佔用
private volatile Node head;//虛擬隊列頭節點
private volatile Node tail;//虛擬隊列尾節點
static Unsafe unsafe;
private static long stateOffset;
private static long tailOffset;
public Sync() {
//初始化EMPTY對象,這樣更新tail head的next也更新了
head = tail = EMPTY;
}
//初始化Unsafe工具和offeset
static {
try {
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
unsafe = (Unsafe) field.get(null);
stateOffset = unsafe.objectFieldOffset(Sync.class.getDeclaredField("state"));
tailOffset = unsafe.objectFieldOffset(Sync.class.getDeclaredField("tail"));
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
public boolean compareAndSetState(int i,int i1) {
return unsafe.compareAndSwapInt(this,stateOffset,i,i1);
}
private boolean compareAndSetTail(Node expect,Node update) {
return unsafe.compareAndSwapObject(this,tailOffset,expect,update);
}
/**
* 加鎖,加鎖前,嘗試一次獲取鎖
*/
public void lock() {
if (compareAndSetState(0,1)) {
return;// 成功不用加鎖
}
Node node = enqueue();
Node prev = node.prev;
//再次嘗試獲取鎖,需要檢測上一個節點是不是head,按入隊順序加鎖
while (node.prev != head || !compareAndSetState(0,1)) {
unsafe.park(false,0L);
}
//當第一個無阻塞線程結束,喚醒一個線程後就會執行這個。
head = node;
node.thread = null;
node.prev = null;
prev.next = null;
}
/**
* 入隊
*
* @return
*/
private Node enqueue() {
while (true) {
Node t = tail;
Node newNode = new Node(Thread.currentThread(),t);
if (compareAndSetTail(t,newNode)) {
t.next = newNode;
return newNode;
}
}
}
/**
* 解鎖
*/
public void unlock() {
state = 0;
Node next = head.next;
if (next != null) {
unsafe.unpark(next.thread);
}
}
//虛擬隊列節點
private static class Node {
Thread thread;
Node prev;
Node next;
public Node() {
}
public Node(Thread thread,Node prev) {
this.thread = thread;
this.prev = prev;
}
}
}
測試類
import java.util.concurrent.CountDownLatch;
import java.util.stream.IntStream;
/**
* @Author 李雷(KyLin)
* @Desc
* @Date 2019/12/31
*/
public class SyncTest {
public static int count = 0;
public static void main(String[] args) throws InterruptedException {
Sync sync = new Sync();
CountDownLatch countDownLatch = new CountDownLatch(100);
IntStream.range(0,100).forEach(i -> new Thread(() -> {
sync.lock();
try {
IntStream.range(0,1000).forEach(j -> {
count++;
});
}finally {
sync.unlock();
}
countDownLatch.countDown();
},"pt" + i).start());
countDownLatch.await();
System.out.println(count);
}
}