源码分析----Lock锁

总结先写前面: 后面留给你去验证

在AQS当中:
主要有五个值得关注的点:

  • private volatile int state; 锁状态的标志位0 为没有上锁。大于0 表示 加锁 值标识锁重入的次数。
  • cas原子操作 保证了交换锁标志位的值,以及入队列等操作在高并发条件下不会出错。
  • LockSupport 提供了将线程阻塞跟唤醒的操作,避免了无谓的自旋操作。
  • 队列 存储所有阻塞的等待唤醒的线程。以便在释放锁的时候使用LockSuport来唤醒它们。
  • 自旋

1. NonfairSync#lock()

分析阶段

final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

这里面调用了两个方法:

1> 一个cas操作 如果设置成功 那就获得锁 并设置当前持有的线程为当前线程。可以直接获得执行的权利,不用去与其他线程来竞争执行的权利

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

2> 设置 线程为持有线程

    protected final void setExclusiveOwnerThread(Thread thread) {
        exclusiveOwnerThread = thread;
    }

3> 获取锁失败参与锁竞争

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

这里先引入公平锁 非公平锁的概念:

公平锁:所有的锁都通过竞争来获取锁

非公平锁: 锁可以先尝试获取锁 ,如果获取成功就不用参与竞争,第一次获取锁失败就参与竞争。

acquire(1);acquire方法做了两件事:尝试获取锁,尝试失败就放到等待队列里面。

我们先讨论非公平锁,它的tryAcquire:

        final boolean nonfairTryAcquire(int acquires) {
            //拿到当前线程
            final Thread current = Thread.currentThread();
            int c = getState();
            //当前状态是未加锁
            if (c == 0) {
                //acquires = 1 设置成1,尝试获取 获取不到也没事。为true 就是获取锁成功
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }//如果当前线程是执行线程。
            else if (current == getExclusiveOwnerThread()) {
                //每次都 + 1
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                //赋值
                setState(nextc);
                return true;
            }
            //获取锁失败
            return false;
        }

nonfairTryAcquire总结:非公平锁的如果当前锁未被线程持有那就先持有,如果是第二次获取当前锁state++ ,可重入的锁。

我们先分析完添加到队列的方法然后再看如何解锁将state 变为 0 的.
将当前线程创建成一个Node 并且入队列。

    private Node addWaiter(Node mode) {
       //排他锁Node.EXCLUSIVE,创建节点
        Node node = new Node(Thread.currentThread(), mode);
        // Try the fast path of enq; backup to full enq on failure
        // 比较快的方式入队列,放到尾部
        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;
                }
            }
        }
    }

添加到队列不是很复杂,但是下面的才是复杂的。

    //以排他的不间断模式获取已排队的线程。>>>>>>>>>>>>>>>>
    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;
                }
                //获取失败 然后使用LockSupport#park方法阻塞当前线程
                if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
                    //可以中断的自旋
                    interrupted = true;
            }
        } finally {
            //如果获取不到 放弃获取
            if (failed)
                cancelAcquire(node);
        }
    }

2.unlock

    public final boolean release(int arg) {
        //尝试释放锁
        if (tryRelease(arg)) {
            //头结点
            Node h = head;
            //头节点不为空 
            if (h != null && h.waitStatus != 0)
                //解除阻塞
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

尝试释放锁

 protected final boolean tryRelease(int releases) {
            //state --
            int c = getState() - releases;
            //
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            //锁释放了
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            //设置state状态
            setState(c);
            return free;
        }

从线程队列中取出线程然后LockSupport.unpark()解除被阻塞的线程。

    private void unparkSuccessor(Node node) {
        /*
         * If status is negative (i.e., possibly needing signal) try
         * to clear in anticipation of signalling.  It is OK if this
         * fails or if status is changed by waiting thread.
         */
        int ws = node.waitStatus;
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);

        /*
         * Thread to unpark is held in successor, which is normally
         * just the next node.  But if cancelled or apparently null,
         * traverse backwards from tail to find the actual
         * non-cancelled successor.
         */
        //头节点是null的
        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);
    }
Java中的AQS(AbstractQueuedSynchronizer)是实现和同步器的一种重要工具。在AQS中,一个节点表示一个线程,依次排列在一个双向队列中,同时使用CAS原子操作来保证线程安全。当多个线程对于同一资源竞争时,一个节点会被放置在队列的尾部,其他线程则在其之前等待,直到该资源可以被定。 当一个线程调用lock()方法进行定时,它会首先调用tryAcquire()方法尝试获取。如果当前资源尚未被定,则该线程成功获取,tryAcquire()返回true。如果当前资源已被定,则线程无法获取,tryAcquire()返回false。此时该线程就会被加入到等待队列中,同时被加入到前一个节点的后置节点中,即成为它的后继。然后该线程会在park()方法处等待,直到前一个节点释放了,再重新尝试获取。 在AQS中,当一个节点即将释放时,它会调用tryRelease()方法来释放,并唤醒后置节点以重试获取。如果当前节点没有后置节点,则不会发生任何操作。当一个线程在队列头部成功获取和资源时,该线程需要使用release()方法释放和资源,并唤醒等待队列中的后置节点。 总之,AQS中的机制是通过双向等待队列实现的,其中节点表示线程,使用CAS原子操作保证线程安全,并在tryAcquire()和tryRelease()方法中进行定和释放。该机制保证了多线程环境下资源的正确访问和线程的安全执行。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值