• Java并发—利用AQS实现自定义锁


    目录

    什么是AQS

    AQS原理

    利用AQS实现自定义锁


    什么是AQS

    AQS(AbstractQueuedSynchronizer),中文名抽象队列同步器

    AQS定义了一套多线程访问共享资源的同步器框架,主要用来自定义锁和同步器

    AQS原理

    AQS 核心思想:

    • 如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。
    • 如果被请求的共享资源被占用,将暂时获取不到锁的线程加入到阻塞队列中,等待被唤醒和锁的分配

    实现核心思想的的队列:CLH队列

    CLH队列是一个虚拟的双向队列,AQS 是将每条请求共享资源的线程封装成一个 CLH 锁队列的一个结点(Node)来实现锁的分配。

    共享资源用 volatile 关键词修饰,保证线程间的可见性

    1. /**
    2. * The synchronization state.
    3. */
    4. private volatile int state;

    0状态表示空闲,1状态或以上表示不空闲

    共享资源(state)的访问方式有三种:  

    1. getState()   获得共享资源状态
    2. setState()   设置共享资源状态
    3. compareAndSetState() 更改共享资源状态(底层unsafe类)

     源代码如下

    1. /**
    2. * Returns the current value of synchronization state.
    3. * This operation has memory semantics of a {@code volatile} read.
    4. * @return current state value
    5. */
    6. protected final int getState() {
    7. return state;
    8. }
    9. /**
    10. * Sets the value of synchronization state.
    11. * This operation has memory semantics of a {@code volatile} write.
    12. * @param newState the new state value
    13. */
    14. protected final void setState(int newState) {
    15. state = newState;
    16. }
    17. /**
    18. * Atomically sets synchronization state to the given updated
    19. * value if the current state value equals the expected value.
    20. * This operation has memory semantics of a {@code volatile} read
    21. * and write.
    22. *
    23. * @param expect the expected value
    24. * @param update the new value
    25. * @return {@code true} if successful. False return indicates that the actual
    26. * value was not equal to the expected value.
    27. */
    28. protected final boolean compareAndSetState(int expect, int update) {
    29. // See below for intrinsics setup to support this
    30. return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
    31. }

    利用AQS实现自定义锁

    一:首先创建一个类实现Lock接口,它有6个方法需要实现

    1. lock():加锁(不成功进入阻塞队列等待)
    2. lockInterruptibly():是否加锁可打断
    3. tryLock()://加锁(不成功不会进入阻塞队列等待,可以去做其他事情)
    4. tryLock(long time,TimeUnit unit):加锁(规定时间内未获得则放弃加锁)
    5. unlock():释放锁
    6. newCondition():创建条件变量

    二:创建一个内部类,继承AbstractQueuedSynchronizer

    可以根据需求重写具体方法,总共有5种方法

    1. isHeldExclusively():该线程是否正在独占资源。只有用到condition才需要去实现它。
    2. tryAcquire(int):独占方式。尝试获取资源,成功则返回true,失败则返回false。
    3. tryRelease(int):独占方式。尝试释放资源,成功则返回true,失败则返回false。
    4. tryAcquireShared(int):共享方式。尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。
    5. tryReleaseShared(int):共享方式。尝试释放资源,如果释放后允许唤醒后续等待结点返回true,否则返回false。

    三:我需要自定义一个独占锁不可重入具有变量条件的锁

    分析

    • 独占锁:AQS同步器中需要重写独占方式的获取资源tryAcquire(int)和释放资源tryRelease(int)方法
    • 不可重入:AQS同步器需要实现isHeldExclusively():
    • 具有条件变量:AQS同步器中 return new ConditionObject();

    具体代码如下

    1. //自定义锁(不可重入)(独占锁)(条件变量)
    2. class MyLock implements Lock{
    3. //内部类,AQS同步器类
    4. class MySync extends AbstractQueuedSynchronizer{
    5. @Override
    6. protected boolean tryAcquire(int arg) {
    7. if (compareAndSetState(0,1)){
    8. System.out.println("获得锁成功");
    9. //加上了锁,并设置owner为当前线程
    10. setExclusiveOwnerThread(Thread.currentThread());
    11. return true;
    12. }
    13. System.out.println("获得锁失败");
    14. return false;
    15. }
    16. @Override
    17. protected boolean tryRelease(int arg) {
    18. setExclusiveOwnerThread(null);
    19. setState(0);
    20. return true;
    21. }
    22. @Override
    23. protected boolean isHeldExclusively() {
    24. return getState() == 1;
    25. }
    26. public Condition newCondition(){
    27. return new ConditionObject();
    28. }
    29. }
    30. private MySync mySync = new MySync();
    31. @Override //加锁(不成功进入阻塞队列等待)
    32. public void lock() {
    33. mySync.acquire(1);
    34. }
    35. @Override //加锁可打断
    36. public void lockInterruptibly() throws InterruptedException {
    37. mySync.acquireInterruptibly(1);
    38. }
    39. @Override //加锁(不成功不会进入阻塞队列等待,可以去做其他事情)
    40. public boolean tryLock() {
    41. return mySync.tryAcquire(1);
    42. }
    43. @Override //尝试加锁 带时间
    44. public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
    45. return mySync.tryAcquireNanos(1,unit.toNanos(time));
    46. }
    47. @Override //释放锁
    48. public void unlock() {
    49. mySync.release(1);
    50. }
    51. @Override //创建条件变量
    52. public Condition newCondition() {
    53. return mySync.newCondition();
    54. }
    55. }

  • 相关阅读:
    Java 8 的新特性还没用起来,先不要想着升级到 Java 17
    MySQL知识点总结(七)——主从复制、读写分离、高可用
    贪心法解决背包问题
    typora整理markdown笔记
    技术分享 | 某下一代防火墙远程命令执行漏洞分析及防护绕过
    深拷贝和浅拷贝是什么,有什么区别?
    【深入设计模式】装饰模式—什么是装饰模式?装饰模式在源码中的应用
    数据结构与算法训练:第十三弹
    Swift中TableView的原理
    去中心化社交媒体:到底是未来 还是鸡肋?
  • 原文地址:https://blog.csdn.net/m0_46628950/article/details/125899881