多线程第三章-AQS及Lock锁

1, LockSupport类

该类是rt.jar包下的一个工具类,主要作用是挂起和唤醒线程,是创建锁和其他同步类的基础

1.1 主要的方法

  • void park() 如果调用park方法的线程已经拿到了与LockSupport关联的许可证,则调用LockSupport.park()时会马上返回,否则调用线程会被禁止参与线程的调度,也就是会被阻塞挂起
@Slf4j
public class LockSupportTest {

    public static void main(String[] args) {
      log.info("begin lockSupport test");
      LockSupport.park();

      log.info("end lockSupport test");
    }
}
输出结果: begin lockSupport test
  • void unPark(Thread thread)

前言

除了synchronized加锁之外,还有lock锁的方式,这俩种锁有什么区别尼?

synchronized

synchronized锁是非公平的锁,是独占的锁,属于抢占式的锁,而且根据synchronized在类中修饰的位置不同,锁的定义也不一样

lock

可以是公平锁也可以是非公平锁,默认实现是非公平的锁,他的实现方式与synchronized也不同,lock是一种可中断锁,使用lockSupport来做线程的中断,

lock实现方式

猜想:

  • 锁的互斥性,当一个线程在使用时,其他线程是无法使用的,需要一个status 来记录当前线程是否拥有锁,0(无锁), 1(有锁),
  • 没有抢占到锁的线程?-> 释放cpu资源,等待--->唤醒
  • 等待的线程如何存储?->使用那种数据结构来处理(能否插队)
  • 公平和非公平
  • 重入的特性(识别是否是同一个线程)

实现技术方案

  • volatile state (0无锁 ,1有锁,-1代表重入)
  • wait/notify |condition 唤醒线程? LockSupport.park /unpark
  • 双向链表存储等待线程
  • 逻辑层面实现公平与非公平
  • 早某一个地方存储当前获得锁线程的ID,判断下次抢占锁的线程是否是同一个。

lock的实现源码分析

public interface Lock {
    void lock();
}

lock的子类ReentrantLock类的构造函数,根据传值对于公平和非公平锁实现做了定义

 public ReentrantLock() {
        sync = new NonfairSync();
    }

    /**
     * Creates an instance of {@code ReentrantLock} with the
     * given fairness policy.
     *
     * @param fair {@code true} if this lock should use a fair ordering policy
     */
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }
NonfairSync(非公平锁)
    static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;

        /**
         * Performs lock.  Try immediate barge, backing up to normal
         * acquire on failure.
         */
        final void lock() {
             /**
              *  比较并设置值 CAS,这里调用的是AQS中的unsafe.compareAndSwapInt(this, stateOffset, expect, update);
              *  unsafe是个native调用的是机器内存值,这里的核心意思是,取用stateOffset当前的值,传入的值是0和1,拿
              *  0和stateOffset的值做比较,期望的值是0,然后将stateOffset的值更新成1,这里主要java底层借助计算机内存地址
              *  来做的,如何比较的期望值得到了,独占所标记住
              *
              */
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                //尝试获取锁,进入等待序列
                acquire(1);
        }

        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }
 public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
 final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            //获取AQS中volatile 的state值,再次使用CAS来获取锁,如果成功则标记
            int c = getState();
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {//当前线程是否为state=0的线程,将state标记改为1 
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

Node链表

static final class Node {
          static final Node SHARED = new Node();
        /** Marker to indicate a node is waiting in exclusive mode */
        static final Node EXCLUSIVE = null;

        /** waitStatus value to indicate thread has cancelled */
        static final int CANCELLED =  1;
        /** waitStatus value to indicate successor's thread needs unparking */
        static final int SIGNAL    = -1;
        /** waitStatus value to indicate thread is waiting on condition */
        static final int CONDITION = -2;
        /**
         * waitStatus value to indicate the next acquireShared should
         * unconditionally propagate
         */
        static final int PROPAGATE = -3;
        
        volatile int waitStatus;
        volatile Node prev;
        
        volatile Node next;
        //当前节点排队的线程
        volatile Thread thread;
        Node nextWaiter;
        
        final boolean isShared() {
            return nextWaiter == SHARED;
        }

        /**
         * Returns previous node, or throws NullPointerException if null.
         * Use when predecessor cannot be null.  The null check could
         * be elided, but is present to help the VM.
         *
         * @return the predecessor of this node
         */
        final Node predecessor() throws NullPointerException {
            Node p = prev;
            if (p == null)
                throw new NullPointerException();
            else
                return p;
        }

        Node() {    // Used to establish initial head or SHARED marker
        }

        Node(Thread thread, Node mode) {     // Used by addWaiter
            this.nextWaiter = mode;
            this.thread = thread;
        }

        Node(Thread thread, int waitStatus) { // Used by Condition
            this.waitStatus = waitStatus;
            this.thread = thread;
        }
}

addWaiter(Node node)

AQS中的链表采用的是双向链表的结构

private Node addWaiter(Node mode) {
        //构建一个链表节点Node
        Node node = new Node(Thread.currentThread(), mode);
        // Try the fast path of enq; backup to full enq on failure
        //获取尾节点的引用,tail表示链表的尾节点
        Node pred = tail;
        //尾节点不为空,队列已经初始化过了
        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        //初始化队列,直到设置当前节点为尾节点才退出
        enq(node);
        return node;
    }

private Node enq(final Node node) {
        for (;;) {
            Node t = tail;
            if (t == null) { // Must initialize
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }

acquireQueued(final Node node, int arg)

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.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

FairSync(公平锁)

//与非公平锁的区别在于 ,进入锁的时候就1 ,在尝试抢占锁的时候判断当前节点是否是头节点来判断
 static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;

        final void lock() {
            acquire(1);
        }

        /**
         * Fair version of tryAcquire.  Don't grant access unless
         * recursive call or no waiters or is first.
         */
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                  //!hasQueuedPredecessors() 当前节点是否是head节点
                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;
        }
    }
posted @   浪成于微澜之间  阅读(28)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
点击右上角即可分享
微信分享提示