zoukankan      html  css  js  c++  java
  • 并发之AQS原理(一) 原理介绍简单使用

    并发之AQS原理(一)

    如果说每一个同步的工具各有各的强大,那么这个强大背后是一个相同的动力,它就是AQS.

    AQS是什么

    AQS是指java.util.concurrent.locks包里的AbstractQueuedSynchronizer类,抽象的FIFO队列式的同步器,AQS定义了一套多线程访问共享资源的同步器框架

    在了解AQS之前先了解下

    先入先出队列(First Input First Output,FIFO)这是一种传统的按序执行方法,先进入的指令先完成并引退,跟着才执行第二条指令。

    就是通常情况下的排队。

    AQS简单的来说就是使用了一个共享变量来同步状态,该状态由子类去维护,而AQS框架做的是

    • 线程阻塞队列的维护。
    • 线程阻塞和唤醒。
      共享变量的操作通过Unsafe类提供的CAS操作完成的。AQS类主要使用的就是2个方法 acquirerelease

    acquire(获得锁):返回true则放线程成继续执行,否则将线程加入等待队列中,等待唤醒.

    release(释放锁):用于释放锁。释放当前线程队列中的头结点,然后调用唤醒下一个结点的方法。

    AQS的简单使用

    一般的来说AQS的使用方式是继承,用的是模板方法模式。

    下面我们来用一个简单的AQS实现来逐步讲解下AQS的原理。

    EasyLock简单实现AQS

    首先实现一个简单的锁就叫EasyLock吧

    /**
     * 简单不可重入锁实现
     *
     */
    public class EasyLock extends AbstractQueuedSynchronizer {
    
        /**
         * 锁定
         */
        public void lock(){
            acquire(1);
        }
    
        /**
         * 尝试锁定
         */
        public boolean tryLock(){
            return tryAcquire(1);
        }
    
        /**
         * 解锁
         */
        public void unlock(){
            release(1);
        }
    
        /**
         * 是否为锁定
         */
        public boolean isLocked(){
            return isHeldExclusively();
        }
    
        /**
         * 尝试获取锁
         */
        @Override
        protected boolean tryAcquire(int arg) {
            if(compareAndSetState(0,1)){
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }
    
        /**
         * 尝试释放锁
         */
        @Override
        protected boolean tryRelease(int arg) {
            setExclusiveOwnerThread(null);
            setState(0);
            return true;
        }
    }
    

    测试

    
    public class AqsExampleDemo {
        private static final int clientTotal = 30000;
        private static int count = 0;
    
        public static void main(String[] args) throws InterruptedException {
            final EasyLock easyLock = new EasyLock();
            ExecutorService executorService = Executors.newCachedThreadPool();
            CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
            for (int i = 0; i < clientTotal; i++) {
                executorService.execute(() -> {
                    try {
                        easyLock.lock();
                        count++;
                        easyLock.unlock();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    countDownLatch.countDown();
                });
            }
    
            countDownLatch.await();
            System.out.println("统计次数:" + count);
    
        }
    }
    

    可以看出打印的结果是5000,并没有出现并发问题。

    总结

    可以看出AQS的使用非常简单几乎只需要重写 tryAcquire、tryRelease就可以自己实现一个锁。
    下一篇我们讲解下AQS中底层的CLH队列是如何保证其并发性的。

  • 相关阅读:
    python 输出所有列表元素的乘积
    shell 变量赋值运算
    shell 判断变量是否为空
    js 获取地址栏域名以及URL
    python 获取列表大于指定长度的元素
    python 判断列表字符串元素首尾字符是否相同
    python 通过列表元素值截取列表并获取长度
    python 判断两个列表是否有公共元素
    python 获取列表的键值对
    python 判断列表的包含关系
  • 原文地址:https://www.cnblogs.com/yanlong300/p/9772271.html
Copyright © 2011-2022 走看看