多线程学习——AQS

多线程学习——AQS

Scroll Down

多线程学习——AQS

1.AQS的作用

许多控制线程的工具如countdownlantch或者samaphore都内部有个Sync类,继承了AQS
AQS是一个用于构建锁、同步器、协助工具类的工具类(框架),有了AQS,更多的协作工具类都可以很方便被写出来

2.AQS的内部原理

1.state

内部计数器 在不同的线程工具类有不同含义如:在samaphore是许可证,在countdownlantch是倒数的数量。state是volatile修饰的,会被并发地修改,所以所有修改state的方法都要保证线程安全,比如getState,setState 这些都依赖j.u.c.atomic包支持

2.队列

这个队列是用来控制线程抢锁和配合的FIFO队列,队列存放“等待线程”,AQS就是排队管理器,多个线程竞争一把锁的时候,必须要有排队机制将没有得到锁的线程串在一起。当锁释放时,锁管理器会挑选一个合适的线程来占用这个刚刚释放的

3.获取/释放等重要方法

期望协作工具类去实现的获取/释放等重要方法,一般是依赖于state来对线程控制。

3.AQS在常用工具类的使用

1.countdownlantch

1.类结构图:

image.png

2.await方法

image.png
image.png

3.countdown方法

image.png
image.png

4.总结

在countdownlantch代表计数器
调用countdownlantch的await()方法,便会尝试获取“共享锁”,不过一开始是获取不到该锁的,线程会进入等待
而共享锁可获取条件,就是“锁计数器”为0
而锁计数器的初始值为count每当一个线程调用该countdownlantch对象的countdown方法才将锁计数器-1
count个线程调用countDown()之后,锁计数器为0,而之前等待的线程等待获取共享锁的线程才能继续运行。

2.samaphore

1.类结构图

image.png

2.aquire方法

image.png
image.png

3.realse方法

image.png

4.总结

在samaphore中,state表示信号量
看tryAcquire方法,判断nonfairTryAcquireShared大于等于0,代表成功
会先检查是否有足够的信号量,如果不够就会失败,如果够,就会自旋的方式加CAS方式改变state状态,直到改变成功返回正数;如果期间有人改变state状态,导致信号量不足,就会失败

3.ReentrantLock

1.类结构图

image.png

2.lock方法

image.png
image.png

3.unlock方法

总结
在ReentrantLock,state代表重入次数
每次释放锁,先判断是不是当前持有锁的线程释放的,如果不是就抛出异常,如果是就把重入次数减一,如果减到0,就说明完全释放,并把state设置为0
加锁的方法
先判断是不是当前持有锁的线程释放的,如果不是就放入队列等待

4.AQS用法

1.根据需求,想好协作逻辑,实现获取/释放方法

2.内部写一个Sync类继承AbstractQueuedSynchronizer

3.根据独占锁(重写tryAcquire/tryRlease) 还是重入锁(重写tryAcquireShared/tryRleaseSha)重写方法

示例:

import java.util.concurrent.locks.AbstractQueuedSynchronizer;

/**
 * @ClassName: PracticeAqs
 * @Description: AQS简单使用 一次性门闩
 * @author: XP
 */
public class PracticeAqs  {

    private final Sync sync=new Sync();


    public void await(){
        //线程进行等待
        sync.acquireShared(0);
    }
    public void countDown(){
        //线程唤醒
        sync.releaseShared(1);
    }



    private static final class Sync extends AbstractQueuedSynchronizer{

        @Override
        protected int tryAcquireShared(int arg) {

            return getState()==1?1:-1;
        }

        @Override
        protected boolean tryReleaseShared(int arg) {
             setState(1);
            return true;
        }
    }

    public static void main(String[] args) {
        PracticeAqs practiceAqs = new PracticeAqs();
        for (int i = 0; i < 5; i++) {
            new Thread(()->{
                System.out.println("线程"+Thread.currentThread().getName()+"准备工作");
                practiceAqs.await();
                System.out.println("线程"+Thread.currentThread().getName()+"开始工作");
            }).start();
        }

        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        practiceAqs.countDown();
    }

}

5.总结

AQS就是一个可以调度线程的工具类 供我们线程的调度器使用(countdownlantch samaphore等),其中有个valitale修饰的status,其他调度器依赖这个实现线程调度,其中还有个等待队列,由AQS来处理调度,我们使用AQS只需在内部实现AQS类,复写他的方法,根据独占锁(重写tryAcquire/tryRlease) 还是重入锁(重写tryAcquireShared/tryRleaseSha)重写方法