ReentrantLock学习笔记

一、使用方式

//使用方式
ReentrantLock  lock = new ReentrantLock();
        new Thread(()->{
            lock.lock();
            try {
                System.out.println("当前线程名称名称位第一层:" + Thread.currentThread().getName());
                lock.lock();
                try {
                    System.out.println("当前线程名称名称位第二层:" + Thread.currentThread().getName());
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        },"A").start();

        new Thread(()->{
            lock.lock();
            try {
                System.out.println("当前线程名称名称位:" + Thread.currentThread().getName());
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        },"B").start();

上面使用ReentrantLock方式,实现的加锁程序,其中lock()和unlock()是一一对应的,如果缺少一个都会导致程序运行异常。

二、构造方法

    //默认构造函数---》默认使用的是非公平所的方式
    public ReentrantLock() {
        sync = new NonfairSync();
    }

    //带参数的构造函数=====》根据传入的参数true/false,来判断走公平或非公平的方式
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

ReentrantLock的加锁方式分为:公平锁和非公平锁两种模式,默认为非公平的加锁方式,后面具体分析

三、lock()具体分析

1、lock解析

 //公平锁的lock方法
 final void lock() {
            acquire(1);
        }
 
 //非公平锁的lock方法
 final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

公平锁直接调用acquire(1)方法进入排队状态,非公平锁是先获取锁,如果获取失败,则进入队列排队。下面先看一下compareAndSetState(0, 1)这个方法。

2、compareAndSetState(0, 1)

   //AbstractQueuedSynchronizer类中的compareAndSetState方法

   protected final boolean compareAndSetState(int expect, int update) {
        // See below for intrinsics setup to support this
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
    }


    /**
     * The synchronization state. 同步状态标识
     */
    private volatile int state;

这边要引入一个变量state,如果state == 0 的时候,表示没有加锁,如果state>0的时候,表示已经加锁。无法获取锁,compareAndSetState使用的底层CAS的方式进行操作的,比较state变量的值,如果是0直接变成1,加锁完成,然后通过setExclusiveOwnerThread(Thread.currentThread())设置当前线程占有锁,否则,加锁失败,进入队列排队

3、acquire(1)

  public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

acquire方法中,没有具体的落地实现,调用了其他方法,下面我们逐个解析这些方法。

   1)tryAcquire(arg)

    //AbstractQueuedSynchronizer 类中的tryAcquire只有抛出异常的操作,但是在他的实现类中,可以找
    //到对应实现操作
    protected boolean tryAcquire(int arg) {
        throw new UnsupportedOperationException();
    }

    
    //实现类ReentrantLock 中FairSync类的tryAcquire方法(公平锁)
    protected final boolean tryAcquire(int acquires) {
            //获取当前线程
            final Thread current = Thread.currentThread();
            //当前线程的状态,就是前面说的state状态
            int c = getState();
            //如果等于0 ,再一次尝试占有锁,如果获取成功,则直接退出排队
            if (c == 0) {
                //hasQueuedPredecessors()表示没有排队的队列
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            //当前线程是正在执行的线程
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

       //实现类ReentrantLock 中FairSync类的tryAcquire方法(公平锁)
           protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }

       final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

tryAcquire方法中,首先获取state的值等于是0,表示无锁的状态,再次判断hasQueuedPredecessors()是否有配对的队列,这边对于公平锁和非公平锁是有区别的,非公平锁,不调用此方法,没有排队的队列,进入compareAndSetState的方式占有锁;如果state的值大于0,并且正要加锁的线程,就是已经加锁的线程,表示可以重复占有锁,叫可重入锁。然后把state的值+1。

  2)创建队列addWaiter(Node.EXCLUSIVE)

 /**
     * Creates and enqueues node for current thread and given mode.
     *
     * @param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared
     * @return the new node
     */
    private Node addWaiter(Node mode) {
        Node node = new Node(Thread.currentThread(), mode);
        // Try the fast path of enq; backup to full enq on failure
        //把尾节点赋值给pred节点,第一次创建时队列的时候tail尾节点是空的,就会走到enq(node)这个方法里面创建队列
        Node pred = tail;
         //tail节点不为空,表示当前队列已经存在,下面是构建双向队列的过程,跟enq里面的相似
        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        enq(node);
        return node;
    }


    /**
     * Inserts node into queue, initializing if necessary. See picture above.
     * @param node the node to insert
     * @return node's predecessor
     */
    private Node enq(final Node node) {
        for (;;) {
            //尾节点赋值给t
            Node t = tail;
            //第一次创建队列的时候,尾节点tail肯定是为空的,所以要初始化
            if (t == null) { // Must initialize
                //设置头节点的值为空节点,
                if (compareAndSetHead(new Node()))
                    //把尾节点指向头节点 == 空节点不是null
                    tail = head;
            } else {
                //队列存在的情况,把当前节点的前置节点置为t节点
                node.prev = t;
                //把当前节点置为尾节点
                if (compareAndSetTail(t, node)) {
                    //双向队列,把t的下一个节点置为当前节点
                    t.next = node;
                    return t;
                }
            }
        }
    }

把尾节点赋值给pred节点,第一次创建时队列的时候tail尾节点是空的,就会走到enq(node)这个方法里面创建队列,在看下enq这个方法,它是for的死循环方法,这边要注意,构建的双向队列之前,head == tail == null ,在构建队列的时候,第一个节点为空,没有任何数据,在第一个数据放入之后,队列应该是下面这个样子的

 3)设置当前线程阻塞acquireQueued方法

    /**
     * Acquires in exclusive uninterruptible mode for thread already in
     * queue. Used by condition wait methods as well as acquire.
     *
     * @param node the node
     * @param arg the acquire argument
     * @return {@code true} if interrupted while waiting
     */
    final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                //返回当前节点的前一个节点数据
                final Node p = node.predecessor();
                //如果是头节点 并且 尝试再次占有锁
                if (p == head && tryAcquire(arg)) {
                    //成功,把当前线程置为头节点
                    setHead(node);
                    //p节点的下一个节点之空
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }


    private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        //这边的waitStatus默认初始值为空
        int ws = pred.waitStatus;     
        if (ws == Node.SIGNAL)  //         static final int SIGNAL    = -1;
            /*
             * This node has already set status asking a release
             * to signal it, so it can safely park.
             */
            return true;
        if (ws > 0) {
            /*
             * Predecessor was cancelled. Skip over predecessors and
             * indicate retry.
             */
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
            /*
             * waitStatus must be 0 or PROPAGATE.  Indicate that we
             * need a signal, but don't park yet.  Caller will need to
             * retry to make sure it cannot acquire before parking.
             */
            //把waitStatus设置成为 -1
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }

    /**
     * Convenience method to park and then check if interrupted
     *
     * @return {@code true} if interrupted
     */
    private final boolean parkAndCheckInterrupt() {
        //阻塞当前线程 
        LockSupport.park(this);
        return Thread.interrupted();
    }

这边的主要处理逻辑是再次占有线程的和 LockSupport.park(this)来阻塞线程。

至此,ReentrantLock的加锁流程源代码已经梳理完成,其中值得注意的是AbstractQueuedSynchronizer(AQS),不管是公平锁,还是非公平锁,都是在继承AQS类的基础上实现的。AQS的主要思想是state+双向队列控制的。

四、unlock()方法实现

1、unlock方法

 public void unlock() {
        sync.release(1);
    }


    public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

如果释放成功,看一下头节点的数据,上面有说过,头节点是一个没有数据的空的node,它的waitStatus状态在shouldParkAfterFailedAcquire这个方法中,已经置为了-1,所以这边会继续往下走.

2、tryRelease(arg)方法

   //这边就是一个抛出异常的操作,具体实现在ReentrantLock里面
  protected boolean tryRelease(int arg) {
        throw new UnsupportedOperationException();
    }

        
    //ReentrantLock的tryRelease实现
    protected final boolean tryRelease(int releases) {
            //获取当前state的值 ,减去传进来的值,一般值是1
            int c = getState() - releases;
            //当前线程不是占有锁的先,则抛出异常
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            //如果c== 0 ,则设置排他锁为null
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }

这个方法主要是设置state的值

3、unparkSuccessor方法

    private void unparkSuccessor(Node node) {
        //这边的node是头节点,也就是那个空节点,它的状态在目前是-1,
        int ws = node.waitStatus;
        if (ws < 0)
            //把waitStatus的状态置为0;
            compareAndSetWaitStatus(node, ws, 0);

        //s是需要唤醒的线程,也就是头节点的下一个节点
        Node s = node.next;
        if (s == null || s.waitStatus > 0) {
            s = null;
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }
        if (s != null)
            //唤醒线程
            LockSupport.unpark(s.thread);
    }

这边主要是设置节点waitStatus的值,使其恢复为0,并唤醒当前线程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值