自旋锁(spinlock):是指当一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环。
前面在接触 AtomicIntege 类时就接触到了自旋锁,如下
public final int getAndAddInt(Object var1, long var2, int var4) { int var5; do { var5 = this.getIntVolatile(var1, var2); } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4)); return var5; }按照上面是逻辑,可以使用原子引用简单模仿一下自旋锁
import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicStampedReference; public class Test07 { public static void main(String[] args) { Mylock mylock = new Mylock(); new Thread(()->{ mylock.lock(); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }finally { mylock.unLock(); } },"a").start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(()->{ mylock.lock(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } finally { mylock.unLock(); } },"b").start(); } } class Mylock{ private AtomicReference<Thread> atomicReference = new AtomicReference<>(); public void lock(){ Thread thread = Thread.currentThread(); System.out.println(thread.getName()+"===>lock"); //自旋锁 while (!atomicReference.compareAndSet(null,thread)){ } } public void unLock(){ Thread thread = Thread.currentThread(); System.out.println(thread.getName()+"===>unLock"); atomicReference.compareAndSet(thread,null); } }如果某个线程持有锁的时间过长,就会导致其它等待获取锁的线程进入循环等待,消耗CPU。使用不当会造成CPU使用率极高。
上面Java实现的自旋锁不是公平的,即无法满足等待时间最长的线程优先获取锁。不公平的锁就会存在“线程饥饿”问题。
自旋锁不会使线程状态发生切换,一直处于用户态,即线程一直都是active的;不会使线程进入阻塞状态,减少了不必要的上下文切换,执行速度快。 非自旋锁在获取不到锁的时候会进入阻塞状态,从而进入内核态,当获取到锁的时候需要从内核态恢复,需要线程上下文切换。 (线程被阻塞后便进入内核(Linux)调度状态,这个会导致系统在用户态与内核态之间来回切换,严重影响锁的性能)