java锁之简单自旋锁

package lock;

import java.util.concurrent.atomic.AtomicReference;

/**
 * Created by Ethan on 14-5-15.
 *
 * 自旋锁适用于锁竞争不那么激烈的情况,和同步块比较小的情况,由于线程的阻塞和释放都是基于信号量(操作系统课本的汇编示例代码还记得不),
 * 并且有用户态和内核态的频繁切换以及线程上下文切换开销比较大,对于前述的两种情况阻塞和释放竞争锁的线程显得没那么的必要,所以引入了自旋
 * 锁,但是自旋锁也有不好的地方,ABA问题,单核无效(依赖环境),太依赖于当前线程的执行时间,从而不断自旋消耗CPU,JDK1.6之后的hotspot
 * 采用了一种折衷的办法,比如
 * 1、  如果平均负载小于CPUs则一直自旋
 * 2、  如果有超过(CPUs/2)个线程正在自旋,则后来线程直接阻塞
 * 3、  如果正在自旋的线程发现Owner发生了变化则延迟自旋时间(自旋计数)或进入阻塞
 * 4、  如果CPU处于节电模式则停止自旋
 * 5、  自旋时间的最坏情况是CPU的存储延迟(CPU A存储了一个数据,到CPU B得知这个数据直接的时间差)
 * 6、  自旋时会适当放弃线程优先级之间的差异
 * 当然这样做的坏处是当自旋的线程得不到锁时会被插入等待队列尾部,相对不公平,无论是依赖于jvm的synchronized语义还是JUC框架的lock包,
 * 都实现了这种折衷的办法,J.U.C是不依赖JVM的纯JAVA实现,底层基于Unsafe类的cas操作和park/unpark操作(由LockSupport做封装)。锁
 * 和同步器的实现都依赖于AQS,AbstractQueuedSynchronizer,要摸透java锁机制,这里是一个很好的开始(努力ING)
 */
public class SpinLock {
    private AtomicReference<Thread> owner = new AtomicReference<>();

    public void lock(){
        Thread currentThread = Thread.currentThread();
        //循环cas来判断锁是否被占用,第一个为期望值,如果为null说明锁未被占用,设置当前线程占用
        while(owner.compareAndSet(null,currentThread)){
            //这里一般会调用java.util.concurrent.locks.AbstractOwnableSynchronizer的setExclusiveOwnerThread(Thread t)方法来设置独占锁
        }
    }

    public void unlock(){
        Thread currentThread = Thread.currentThread();

        owner.compareAndSet(currentThread,null);
    }
}



你可能感兴趣的:(java锁之简单自旋锁)