几种自旋锁SpinLock,TicketLock,CLHLock,以及可重入实现要点,非阻塞锁实现要点

来源:互联网 发布:淘宝白夜追凶视频电话 编辑:程序博客网 时间:2024/04/30 14:39

最核心的东西:synchronization state,同步状态:指示当前线程是否可以proceed还是需要wait的状态。

1.普通SpinLock (支持可重入的版本)

class SpinLock {// use thread itself as  synchronization stateprivate AtomicReference<Thread> owner = new AtomicReference<Thread>(); private int count = 0; // reentrant count of a thread, no need to be volatilepublic void lock() {Thread t = Thread.currentThread();if (t == owner.get()) { // if re-enter, increment the count.++count;return;}while (owner.compareAndSet(null, t)) {} //spin}public void unlock() {Thread t = Thread.currentThread();if (t == owner.get()) { //only the owner could do unlock;if (count > 0) --count; // reentrant count not zero, just decrease the counter.else {owner.set(null);// compareAndSet is not need here, already checked}}}}


为什么用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;}};}@Overridepublic void lock() {QNode qnode = myNode.get();qnode.locked = true;QNode pred = tail.getAndSet(qnode);myPred.set(pred);while (pred.locked) {}}@Overridepublic 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;    }}




0 0
原创粉丝点击