何为自旋锁自旋锁是为实现爱护共享资源而提出的一种锁机制。自旋锁与Java中的synchronized和Lock不同,不会引起调用线程阻塞睡眠。如果有线程持有自旋锁,调用线程就会始终循环检测锁的状态,直到其余线程开释锁,调用线程才进行自旋,获取锁。
自旋锁的劣势和缺点自旋锁的长处很显著:自旋锁不会使线程状态进行切换,始终处于用户态,即不会频繁产生上下文切换,执行速度快,性能高。
正是因为其不进行上下文切换的长处,也使得某些状况下,缺点也很显著:如果某个线程持有锁的工夫过长,或者线程间竞争强烈,就会导致某些期待获取锁的线程进入长时间循环期待,耗费CPU,从而造成CPU占用率极高。
自旋锁的实用场景自旋锁实用于被锁代码块执行工夫很短,即加锁工夫很短的场景。
常见自旋锁实现比拟有名的四种自旋锁:传统自旋锁SpinLock,排队自旋锁TicketSpinLock,CLH自旋锁,MCS自旋锁。这四种自旋锁的基本原理都是在CAS的根底上实现的,各有各的特点,且逐渐优化。
SpinLock传统自旋锁的劣势和有余实现原理SpinLock原理很简略,多个线程循环CAS批改一个共享变量,批改胜利则进行自旋获取锁。
代码实现实现接口参考了Java的Lock,外围办法在tryAcquire和tryRelease。获取锁的形式实现了自旋,可中断自旋,自旋超时中断,不自旋。共享变量state不仅作为锁的状态标记(state=0锁闲暇,state>0有线程持有锁),同时可作为自旋锁重入的次数。exclusiveOwnerThread记录以后持有锁的线程。
public class SpinLock implements Lock { protected volatile int state = 0; private volatile Thread exclusiveOwnerThread; @Override public void lock() { for(;;) { //直到获取锁胜利,才完结循环 if (tryAcquire(1)) { return; } } } @Override public void lockInterruptibly() throws InterruptedException { for(;;) { if (Thread.interrupted()) { //有被中断 抛异样 throw new InterruptedException(); } if (tryAcquire(1)) { return; } } } /** * 返回获取锁的后果,不会自旋 * @return */ @Override public boolean tryLock() { return tryAcquire(1); } /** * 返回获取自旋锁的后果,会自旋一段时间,超时后进行自旋 * @param time * @param unit * @return * @throws InterruptedException */ @Override public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { long nanosTimeout = unit.toNanos(time); if (nanosTimeout <= 0L) { return false; } final long deadline = System.nanoTime() + nanosTimeout; for(;;) { if (Thread.interrupted()) { //有被中断 抛异样 throw new InterruptedException(); } if (tryAcquire(1)) { return true; } nanosTimeout = deadline - System.nanoTime(); if (nanosTimeout <= 0L) { //超时自旋,间接返回false return false; } } } @Override public void unlock() { tryRelease(1); } @Override public Condition newCondition() { throw new UnsupportedOperationException(); } public int getState() { return state; } /** * 获取持有锁的以后线程 * @return */ public Thread getExclusiveOwnerThread() { return exclusiveOwnerThread; } /** * 获取以后线程重入次数 * @return */ public int getHoldCount() { return isHeldExclusively() ? getState() : 0; } /** * 开释锁 * @param releases * @return */ protected boolean tryRelease(int releases) { int c = getState() - releases; Thread current = Thread.currentThread(); if (current != getExclusiveOwnerThread()) //不是以后线程,不能unLock 抛异样 throw new IllegalMonitorStateException(); boolean free = false; if (c <= 0) { //每次减一,c = 0,证实没有线程持有锁了,能够开释了 free = true; c = 0; setExclusiveOwnerThread(null); System.out.println(String.format("spin un lock ok, thread=%s;", current.getName())); } //排它锁,只有以后线程才会走到这,是线程平安的 批改state setState(c); return free; } /** * 获取锁 * @param acquires * @return */ protected boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { //若此时锁空着,则再次尝试抢锁 if (compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); System.out.println(String.format("spin lock ok, thread=%s;", current.getName())); return true; } } //若以后持锁线程是以后线程(重入性) else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); //重入 setState(nextc); System.out.println(String.format("spin re lock ok, thread=%s;state=%d;", current.getName(), getState())); return true; } return false; } /** * 判断以后线程是否持有锁 * @return */ protected final boolean isHeldExclusively() { return getExclusiveOwnerThread() == Thread.currentThread(); } protected void setState(int state) { this.state = state; } protected void setExclusiveOwnerThread(Thread exclusiveOwnerThread) { this.exclusiveOwnerThread = exclusiveOwnerThread; } protected final boolean compareAndSetState(int expect, int update) { return unsafe.compareAndSwapInt(this, stateOffset, expect, update); } protected static final Unsafe getUnsafe() { try { //不能够间接应用Unsafe,须要通过反射,当然也能够间接应用atomic类 Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); theUnsafe.setAccessible(true); Unsafe unsafe = (Unsafe) theUnsafe.get(null); if (unsafe != null) { return unsafe; } } catch (NoSuchFieldException | IllegalAccessException e) { e.printStackTrace(); } throw new RuntimeException("get unsafe is null"); } private static final Unsafe unsafe = getUnsafe(); private static final long stateOffset; static { try { stateOffset = unsafe.objectFieldOffset (SpinLock.class.getDeclaredField("state")); } catch (Exception ex) { throw new Error(ex); } }SpinLock的特点劣势:SpinLock实现原理简略,线程间没有频繁的上下文切换,执行速度快,性能高。缺点:SpinLock是不偏心的,无奈满足等待时间最长的线程优先获取锁,造成 “线程饥饿”。缺点:因为每个申请自旋锁的处理器均在一个全局变量上自旋检测,系统总线将因为处理器间的缓存同步而导致沉重的流量,从而升高了零碎整体的性能。因为传统自旋锁无序竞争的实质特点,内核执行线程无奈保障何时能够取到锁,某些执行线程可能须要期待很长时间,导致“不偏心”问题的产生。有两个方面的起因:
...