通过调试来从源码上理解AbstractQueuedSynchronizer(AQS)

这篇文章首先对AQS进行详细解析,作为上文,还有下文,专门针对几个以AQS为低层进行实现的工具类进行讲解,通过这篇文章,能够对AQS有更好的理解。

AbstractQueuedSynchronizer(AQS)是java并发包中很多并发工具类的基础,比如java.util.concurrent.locks.ReentrantLock重入锁、java.util.concurrent.locks.ReentrantReadWriteLock读写锁、java.util.concurrent.Semaphore信号量、java.util.concurrent.CountDownLatch。它们都是在自己的类中又定义了内部类Sync类继承AbstractQueuedSynchronizer,然后重写其中的一些protected修饰的方法。AQS内部定义了一些模板方法,这些模板方法会调用前面说到的protected修饰的方法。通过模板方法设计模式,所有的这些高级的同步组件都不需要再重写关于线程排队、等待、唤醒等相关代码,这些代码被AQS抽离出来实现了重用。

下面我们从独占锁ReentrantLock入手,来看看AQS内部到底做了哪些事情。首先是一段很简单的代码,实现了三个线程同时调用被ReentrantLock加锁保护的方法sayHello。

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class AQSDebug {
   
    private Lock lock = new ReentrantLock();

    private void sayHello() {
   
        lock.lock();
        System.out.println("hello");
        lock.unlock();
    }

    public static void main(String[] args) {
   
        AQSDebug aqsDebug = new AQSDebug();
        new Thread(aqsDebug::sayHello, "first").start();
        new Thread(aqsDebug::sayHello, "second").start();
        new Thread(aqsDebug::sayHello, "third").start();
    }
}

为了实现三个线程相互抢占的效果,我们使用多线程断点调试的方法(参考这篇文章),让线程first先获取到锁,然后切换到线程second或者third进行调试,这样就能够看到因为获取不到锁,而加入同步队列,等待,被唤醒的过程。

切换到线程second,我们来看一下,会走到java.util.concurrent.locks.ReentrantLock.NonfairSync#lock方法,NonfairSync继承了Sync,Sync继承了AbstractQueuedSynchronizer。在lock方法中,首先尝试用CAS的方式,将AQS中用来表示同步状态的volatile变量state设置为1,这里用CAS的方式是保证原子性,因为可能有多个线程同时来争抢。

static final<
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java中的AQSAbstractQueuedSynchronizer)是实现锁和同步器的一种重要工具。在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、付费专栏及课程。

余额充值