1.普通SpinLock (支持可重入的版本)
- class SpinLock {
-
- private AtomicReference<Thread> owner = new AtomicReference<Thread>();
- private int count = 0;
- public void lock() {
- Thread t = Thread.currentThread();
- if (t == owner.get()) {
- ++count;
- return;
- }
- while (owner.compareAndSet(null, t)) {}
- }
- public void unlock() {
- Thread t = Thread.currentThread();
- if (t == owner.get()) {
- if (count > 0) --count;
- else {
- owner.set(null);
- }
- }
- }
- }
为什么用Thread 本身当 同步状态而不是一个简单的boolean flag? 因为可以携带更多信息(当前owning thread)
1) 支持可重入,判断是否是重入,重入不需要改变同步状态,而只需要计数
2)确保只有锁的拥有者才能做unlock
2 TicketLock
思路:类似银行办业务,先取一个号,然后等待叫号叫到自己。好处:保证FIFO,先取号的肯定先进入。而普通的SpinLock,大家都在转圈,锁释放后谁刚好转到这谁进入。
- class TicketLock {
- private AtomicInteger serviceNum = new AtomicInteger(0);
- private AtomicInteger ticketNum = new AtomicInteger(0);
- private static final ThreadLocal<Integer> myNum = new ThreadLocal<Integer>();
- public void lock () {
- myNum.set(ticketNum.getAndIncrement());
- while (serviceNum.get() != myNum.get()) {};
- }
- public void unlock() {
- serviceNum.compareAndSet(myNum.get(), myNum.get() + 1);
- }
- }
3 CLHLock
CLH好处
1)公平,FIFO,先来后到的顺序进入锁
2)而且没有竞争同一个变量,因为每个线程只要等待自己的前继释放就好了。


- public class CLHLock implements Lock {
- AtomicReference<QNode> tail = new AtomicReference<QNode>(new QNode());
- ThreadLocal<QNode> myPred;
- ThreadLocal<QNode> myNode;
-
- public CLHLock() {
- tail = new AtomicReference<QNode>(new QNode());
- myNode = new ThreadLocal<QNode>() {
- protected QNode initialValue() {
- return new QNode();
- }
- };
- myPred = new ThreadLocal<QNode>() {
- protected QNode initialValue() {
- return null;
- }
- };
- }
-
- @Override
- public void lock() {
- QNode qnode = myNode.get();
- qnode.locked = true;
- QNode pred = tail.getAndSet(qnode);
- myPred.set(pred);
- while (pred.locked) {
- }
- }
-
- @Override
- public void unlock() {
- QNode qnode = myNode.get();
- qnode.locked = false;
- myNode.set(myPred.get());
- }
- }
4 阻塞锁的实现
CLH每个线程lock 的时候 spin on 它的前驱,不park,unlock的时候,只需要修改自身状态,不需要唤醒(unpark)后继线程。而阻塞方式下,lock的时候需要park自己,unlock的时候要 unpark后继
- package lock;
-
- import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
- import java.util.concurrent.locks.LockSupport;
-
- public class CLHLock1 {
- public static class CLHNode {
- private volatile Thread isLocked;
- }
-
- @SuppressWarnings("unused")
- private volatile CLHNode tail;
- private static final ThreadLocal<CLHNode> LOCAL = new ThreadLocal<CLHNode>();
- private static final AtomicReferenceFieldUpdater<CLHLock1, CLHNode> UPDATER =
- AtomicReferenceFieldUpdater.newUpdater(CLHLock1.class,
- CLHNode.class, "tail");
-
- public void lock() {
- CLHNode node = new CLHNode();
- LOCAL.set(node);
- CLHNode preNode = UPDATER.getAndSet(this, node);
- if (preNode != null) {
- preNode.isLocked = Thread.currentThread();
- LockSupport.park(this);
- preNode = null;
- LOCAL.set(node);
- }
- }
-
- public void unlock() {
- CLHNode node = LOCAL.get();
- if (!UPDATER.compareAndSet(this, node, null)) {
- System.out.println("unlock\t" + node.isLocked.getName());
- LockSupport.unpark(node.isLocked);
- }
- node = null;
- }
- }