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

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

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

class SpinLock {
	// use thread itself as  synchronization state
	private AtomicReference owner = new AtomicReference(); 
	private int count = 0; // reentrant count of a thread, no need to be volatile
	public 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 myNum = new ThreadLocal();
	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 tail = new AtomicReference(new QNode());
	ThreadLocal myPred;
	ThreadLocal myNode;

	public CLHLock() {
		tail = new AtomicReference(new QNode());
		myNode = new ThreadLocal() {
			protected QNode initialValue() {
				return new QNode();
			}
		};
		myPred = new ThreadLocal() {
			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 LOCAL   = new ThreadLocal();
    private static final AtomicReferenceFieldUpdater 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;
    }
}




你可能感兴趣的:(并发,多线程)