基于AQS、CAS、LockSupport手写Lock锁

1.AQS 原理

AQS核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中。

CLH(Craig,Landin,and Hagersten)队列是一个虚拟的双向队列(虚拟的双向队列即不存在队列实例,仅存在结点之间的关联关系)。AQS是将每条请求共享资源的线程封装成一个CLH锁队列的一个结点(Node)来实现锁的分配。

看个AQS(AbstractQueuedSynchronizer)原理图: 

AQS使用一个int成员变量来表示同步状态,通过内置的FIFO队列来完成获取资源线程的排队工作。AQS使用CAS对该同步状态进行原子操作实现对其值的修改。

private volatile int state;//共享变量,使用volatile修饰保证线程可见性
状态信息通过protected类型的getState,setState,compareAndSetState进行操作

//返回同步状态的当前值
protected final int getState() {
        return state;
}
 // 设置同步状态的值
protected final void setState(int newState) {
        state = newState;
}
//原子地(CAS操作)将同步状态值设置为给定值update如果当前同步状态的值等于expect(期望值)
protected final boolean compareAndSetState(int expect, int update) {
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

 2.简单介绍AQS的原理,下面我们基于AQS、CAS、LockSupport手写一把锁。关于CAS的介绍可以移步本人博客参考(基于CAS手写一把锁_coder86的博客-CSDN博客

import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.LockSupport;

public class MyLock {
    /**
     * 锁的状态 0===没有线程获取到锁 1表示已经有线程获取到锁
     */
    private AtomicInteger lockState = new AtomicInteger(0);
    /**
     * 获取到锁的线程
     */
    private Thread getLockThread = null;
    /**
     * 没有获取到锁的线程
     */
    private ConcurrentLinkedDeque<Thread> concurrentLinkedDeque = new ConcurrentLinkedDeque<Thread>();

    /**
     * 获取锁
     */
    public void lock() {
        acquire();
    }

    public boolean acquire() {
        for (; ; ) {
            System.out.println(Thread.currentThread().getName()+"cas操作");
            if (compareAndSet(0, 1)) {
                // 获取锁成功
                getLockThread = Thread.currentThread();
                return true;
            }
            // 获取锁失败
            Thread thread = Thread.currentThread();
            concurrentLinkedDeque.add(thread);
            // 阻塞
            LockSupport.park();
        }
    }

    private boolean compareAndSet(int expect, int update) {
        return lockState.compareAndSet(expect, update);
    }

    /**
     * 释放锁
     */
    public boolean unLock() {
        if (getLockThread == null) {
            return false;
        }
        if (getLockThread == Thread.currentThread()) {
            boolean result = compareAndSet(1, 0);
            if (result) {
                // 公平锁唤醒:
                Thread first = concurrentLinkedDeque.getFirst();
                System.out.println(first.getName()+",被唤醒");
                LockSupport.unpark(first);
                return true;
            }
        }

        return false;
    }
}

编写测试类验证

public class Test07 {
    public static void main(String[] args) throws InterruptedException {
        MyLock myLock = new MyLock();
        myLock.lock();
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "start");
            myLock.lock();
            System.out.println(Thread.currentThread().getName() + "end");
        }).start();
        Thread.sleep(1000);
        myLock.unLock();
    }
}

运行过程:主线程先获取到锁,子线程未获取到锁进入阻塞状态,同时子线程对象被放入队列,1s后主线程释放了锁,执行方法LockSupport.unpark(Thread t)唤醒子线程,子线程重新获取到了锁,得以继续执行。

AQS核心思想就是多个线程争抢一把锁,同一时刻只有一个线程能获取到锁,其他线程进入阻塞状态,同时阻塞的线程被存到一个队列中,当锁被释放时,从队列中取出线程对象,LockSupport.unpark(Thread t);表示将线程从锁阻状态变成就绪状态。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值