AQS源码解析1.前导_手写一个简单的ReentrantLock

参考小刘讲源码

1.lock流程图

在这里插入图片描述

2.unlock

unlock的流程相对简单,就是将state置为0将exclusiveOwnerThread置为NULL(不考虑重入不考虑失败),然后将队列中下一个节点唤醒。

3.代码

定义一个Lock接口

public interface Lock {
    
    
    void lock();

    void unlock();
}

实现Lock接口实现自定义锁

import sun.misc.Unsafe;
import java.lang.reflect.Field;
import java.util.concurrent.locks.LockSupport;


/*
 *  @Author shstart
 *  @Date 2021-12-1
 * */
public class MiniReentrantLock implements Lock {
    
    

    /**
     * 锁是什么? 资源 -> state
     * state = 0  未加锁
     * state > 0 当前lock是加锁状态
     * 这里使用volatile修饰,保证内存的可见性。
     */
    private volatile int state;


    /**
     * 独占模式? 同一时刻只能有一个线程可以持有锁,其他的线程,未获取到锁会被阻塞。
     * 当前持有锁的线程。
     */
    private Thread exclusiveOwnerThread;


    /**
     * 需要有两个引用去维护阻塞队列。
     * head 指向队列的头结点 tail指向队列的尾结点
     */
    private Node head; //head节点对应的线程 就是当前获取锁的线程
    private Node tail; //尾结点


    /**
     * 阻塞的线程是要被唤醒的。
     * 这里将阻塞的线程封装成一个Node节点,放到FIFO双向队列中。
     */
    static class Node {
    
    
        Node prev; //前置节点
        Node next; //后置节点
        Thread thread; //封装的线程本身。

        public Node() {
    
    
        }

        public Node(Thread thread) {
    
    
            this.thread = thread;
        }

        public Node(Thread thread, Node prev, Node next) {
    
    
            this.thread = thread;
            this.prev = prev;
            this.next = next;
        }
    }


    /**
     * 获取锁
     * 假设锁被占用,则会阻塞当前线程,直到抢占到锁为止
     * <p>
     * lock的过程:
     * 情景1: 线程进来之后发现,当前state == 0, 这个时候就很幸运,直接抢锁
     * 情景2: 线程进来之后发现,当前state > 0,  这个时候就需要将当前线程入队。
     */
    @Override
    public void lock() {
    
    
        /*
         *  第一次获取到锁时 state =1
         *  第n次重入时 state = n
         * */
        acquire(1);
    }


    /**
     * 竞争资源
     * 1.尝试获取锁,成功则占用锁返回
     * 2.抢占锁失败,阻塞当前线程
     */
    private void acquire(int arg) {
    
    
        //尝试获取锁失败
        if (!tryAcquire(arg)) {
    
    
            //入队
            Node node = addWaiter();
            //在队列中尝试获取锁
            acquireQueued(node, arg);
        }
    }

    /**
     * 尝试抢占锁失败,需要做什么?
     * 1.需要当前线程封装为node,加入到队列
     * 2.需要将当前线程park(挂起),等待被唤醒
     * <p>
     * 唤醒后呢?
     * 1.检查当前node节点是否为head.next
     * (注意: head节点是已经获取了锁的节点,这时候队列中只有
     * head.next这个节点才有资格去抢占锁,其他线程都没有抢占的权限)
     * 2.抢占
     * 成功: 将当前node设置为head,将老的head出队,返回到业务层面
     * 失败  继续park,等待被唤醒
     * <p>
     * -----------
     * 添加到阻塞队列中的逻辑 addWaiter()
     * 在队列中竞争资源的逻辑 acquireQueued()
     */


    private void acquireQueued(Node node, int arg) {
    
    
        //只有当前node成功获取到锁后才会跳出自旋
        for (; ; ) {
    
    
            Node pred = node.prev;
            //只有当前节点是当前节点是head.next 才能尝试获取锁
            if (pred == head && tryAcquire(arg)) {
    
    
                //设置头结点
                setHead(node);
                //help GC
                pred.next = null;
                //抢到锁 直接return;
                return;
            }
            //不符合抢锁条件 挂起当前线程
            System.out.println("线程: " + Thread.currentThread().getName() + " .挂起");
            LockSupport.park();
            System.out.println("线程: " + Thread.currentThread().getName() + " .唤醒");
            //什么时候唤醒被park的线程呢? unlock过程
        }
    }


    /*
     *  将当前线程入队,保证当前线程已经入队成功
     * */
    private Node addWaiter() {
    
    
        Thread thread = Thread.currentThread();
        Node node = new Node(thread);

        /*
         *  如何入队 ?
         *  1.找到node的前置节点 x
         *  2.更新node.prev = x;
         *  3.CAS更新tail 为 node
         *  4.更新x.next = node;
         * */

        //队列中有元素
        Node x = tail;
        if (x != null) {
    
    
            node.prev = x;
            if (compareAndSetTail(x, node)) {
    
    
                x.next = node;
                return node;
            }
        }

        /**
         * 执行到这里有几种情况?
         * 1.tail == null 队列是空队列
         * 2.CAS设置node为tail时失败,存在并发
         */

        //自旋入队(一定会成功入队)
        enq(node);
        return node;
    }

    /**
     * 自旋入队 只有成功入队后才返回
     **/
    private void enq(Node node) {
    
    
        for (; ; ) {
    
    

            /**
             * >> 第一种情况:队列是空队列
             * >> 当前线程是第一个抢占锁失败的线程
             * 因为第一个获取锁的线程a直接CAS成功时不会构造node,所以当前线程作为a的第一个后驱线程,
             *  需要在队列中补充一个node作为head节点,(head节点任何时候都表示当前获取锁的线程)
             *  下次自旋才会去正在的添加节点
             **/
            if (tail == null) {
    
    
                //补充节点成功 (这里的期望值是NULL)
                if (compareAndSetHead(new Node())) {
    
    
                    tail = head;
                }
            } else {
    
    
                //当前队列中刚已经有node了,需要CAS方式追加一个node
                Node x = tail;
                if (x != null) {
    
    
                    node.prev = x;
                    //CAS设置成功。
                    if (compareAndSetTail(x, node)) {
    
    
                        x.next = node;
                        //入队成功后直接return。
                        return;
                    }
                }
            }
        }
    }


    /**
     * 尝试获取锁
     * true -> 抢占成功
     * false -> 抢占失败
     */
    private boolean tryAcquire(int arg) {
    
    
        if (state == 0) {
    
    
            /**
             *  当前state == 0,是否可以直接抢锁呢? 不可以。
             *  这里模拟的是一个公平锁,首先得判断同步队列中是否有节点,有节点的话这里必须要先排队。
             *  所以这里必须判断队列中是否有节点,没有节点的话才能试图去抢占锁。如果有节点直接失败
             *  通过CAS的方式去抢占锁,因为同一时刻可能有很多线程都在抢占锁。
             * */
            if (!hasQueuedPredecessor() && compareAndSetState(0, arg)) {
    
    
                //走到这里说明抢占锁成功了,需要将exclusiveOwnerThread设置为当前线程。
                this.exclusiveOwnerThread = Thread.currentThread();
                return true;
            }

            /**
             *   判断当前线程就是持有锁的线程,所以这里就是重入的逻辑
             * */
        } else if (Thread.currentThread() == this.exclusiveOwnerThread) {
    
    
            //计算state的下一个状态。
            int c = getState() + arg;
            /**
             *  这里设置state不需要加锁,因为当前线程就是加锁的线程
             *  即这里不存在并发
             */
            this.state = c;
            return true;
        }

        /**
         *  什么时候会加锁失败?
         *  1.state = 0 && (队列中有等待的线程 || CAS设置state失败(有并发))
         *  2.(state > 0 && 当前线程不是获取锁的线程)
         * */
        return false;
    }


    /**
     * true -> 表示当前线程前面有等待者线程
     * false -> 当前线程前没有其他等待者线程
     */
    private boolean hasQueuedPredecessor() {
    
    
        Node h = head;
        Node t = tail;
        Node s;
        return h != t && ((s = h.next) == null || s.thread != Thread.currentThread());
    }


    /**
     * 释放锁
     */
    @Override
    public void unlock() {
    
    
        release(1);
    }

    public void release(int arg) {
    
    
        //条件成立 说明线程已经完全释放锁了,
        if (tryRelease(arg)) {
    
    
            Node head = this.head;
            //唤醒head.next
            if (head.next != null) {
    
    
                unparkSuccessor(head);
            }
        }
    }

    //唤醒节点
    private void unparkSuccessor(Node node) {
    
    
        Node s = node.next;
        if (s != null && s.thread != null) {
    
    
            LockSupport.unpark(s.thread);
        }
    }


    /*
     *  完全释放成功,返回true
     *  否则说明state > 0, 返回false。
     * */

    public boolean tryRelease(int arg) {
    
    
        int c = getState() - arg;

        //当先线程不是获取锁的线程 直接抛出异常
        if (getExclusiveOwnerThread() != Thread.currentThread()) {
    
    
            throw new RuntimeException("fuck you !!! must getLock");
        }

        //如果执行到这里存在并发吗? 不存在 只有一个线程(当前持有锁的线程)会来到这里

        //c == 0,说明状态已经被完全释放了,
        if (c == 0) {
    
    
            //独占线程置为NULL
            this.exclusiveOwnerThread = null;
            //状态设置为0
            this.state = 0;
            return true;
        }

        //当前线程没有完全释放锁 (c != 0)
        this.state = c;
        return false;
    }

    /**
     * 设置队列的新头结点
     */
    private void setHead(Node node) {
    
    
        this.head = node;
        //因为当前节点已经是获取锁的线程了,直接将引用的thread属性置为NULL即可
        node.thread = null;
        //将前置节点也置为NULL。
        node.prev = null;
    }

    /**
     * 基于CAS,得到state、head、tail属性的setter方法为其赋值
     */
    private static final Unsafe unsafe;
    private static final long stateOffset;
    private static final long headOffset;
    private static final long tailOffset;

    static {
    
    
        try {
    
    
            Field f = Unsafe.class.getDeclaredField("theUnsafe");
            f.setAccessible(true);
            unsafe = (Unsafe) f.get(null);

            stateOffset = unsafe.objectFieldOffset
                    (MiniReentrantLock.class.getDeclaredField("state"));
            headOffset = unsafe.objectFieldOffset
                    (MiniReentrantLock.class.getDeclaredField("head"));
            tailOffset = unsafe.objectFieldOffset
                    (MiniReentrantLock.class.getDeclaredField("tail"));

        } catch (Exception ex) {
    
    
            throw new Error(ex);
        }
    }

    private final boolean compareAndSetHead(Node update) {
    
    
        return unsafe.compareAndSwapObject(this, headOffset, null, update);
    }

    private final boolean compareAndSetTail(Node expect, Node update) {
    
    
        return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
    }

    protected final boolean compareAndSetState(int expect, int update) {
    
    
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
    }

    public int getState() {
    
    
        return state;
    }

    public Thread getExclusiveOwnerThread() {
    
    
        return exclusiveOwnerThread;
    }

    public Node getHead() {
    
    
        return head;
    }

    public Node getTail() {
    
    
        return tail;
    }
}

猜你喜欢

转载自blog.csdn.net/qq_46312987/article/details/121663516