高并发的核心 - AQS【哪些琐是基于AQS来实现的】

数据湖
• 阅读 136

1.AQS是什么?

AQS(AbstractQueuedSynchronizer)抽象队列同步器,是JDK下提供的一套实现基于FIFO等待队列的阻塞锁和相关同步器的一个同步框架.

基础:

  1. 线程:掌握Java线程
  2. 锁:明白什么是锁
  3. 高并发(可选)

在Java新版本 里实现锁的框架

Public class Main {
    public static int m = 0;//静态成员变量
    
    public static void main(String[] args) throws Exception{
        Thread[] threads = new Thread[100];//定义一个线程数组
        for(int i=0; i<threads.length; i++) {
            threads[i] = new Thread(()->{               
                for(int j=0; j<100; j++) m++; 
            });
        }
        for(Thread t : threads) t.start();//开始运行所有线程
        for(Thread t : threads) t.join();//等待所有线程结束
        System.out.println(m);
    }
}
/*这时候运行的结果并不准确*/

/*值会小于10000*/ 

如果这么写,运行结果并不准确;其原因是:

如果m=0一直++到100,当第一个线程m++,另一个线程也拿过来加,当第一个线程一直加到99时返回,而另一个线程也加到99返回,这样一来m本来应该是+2返回,但只加了1就返回 ;

那么如何解决这个问题:加锁,

加锁概念

是把锁在Main.class身上,锁的是对象,监听的是对象。

给其加锁后,当一个线程执行时,另一个线程也要来执行时,发现锁被占用,就会发生阻塞,直到被锁上的线程执行完。

其他线程在争这把锁,谁争到谁就执行,这样就避免了俩个线程同时执行。

2.synchronized() 同步锁

  1. 为什么使用synchronized在并发编程中存在线程安全问题,主要原因:1.存在共享数据 2.多线程共同操作共享数据.关键词synchronized可以保证在同一时刻只有一个线程可以执行某个方法或某个代码块,同时synchronized可以保证一个线程的变化可见.
  2. synchronized的三种应用方式普通同步方法(实例),锁是当前实例对象,进入同步代码前要获得当前实例的锁。静态同步方法,锁是当前类的class对象,进入同步代码前要获得当前类对象的锁。同步方法块,锁是括号里面的对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。
  3. synchronized的作用确保线程互斥访问同步代码。保证共享变量的修改能够及时可见。解决重排序问题。

例:

Public class Main {
    public static int m = 0;//静态成员变量
    
    public static void main(String[] args) throws Exception{
        Thread[] threads = new Thread[100];//定义一个线程数组
        for(int i=0; i<threads.length; i++) {
            threads[i] = new Thread(()->{
                /*这里锁的是对象*/
                synchronized (Main.class){
                for(int j=0; j<100; j++) m++;
                }
            });
        }
        for(Thread t : threads) t.start();//开始运行所有线程
        for(Thread t : threads) t.join();//等待所有线程结束
        System.out.println(m);
    }
} 

3.ReentrantLock() 重入锁

ReentrantLock底层实现就是AQS

ReentranLock特性(对比synchronized)

  • 尝试获得锁
  • 获取到锁的线程能够响应中断

例:

Public class Main {
    public static int m = 0;//静态成员变量
    public static Lock lock = new ReentrantLock();//这里定义一把锁
    public static void main(String[] args) throws Exception{
        Thread[] threads = new Thread[100];//定义一个线程数组
        for(int i=0; i<threads.length; i++) {
            threads[i] = new Thread(()->{
             /*必须要用try finally*/
                try{
                 lock.lock();//上锁
                    for(int j=0; j<100; j++) m++;
             } finally{
                 lock.unlock();//解锁
             }
                            
            });
        }
        for(Thread t : threads) t.start();//开始运行所有线程
        for(Thread t : threads) t.join();//等待所有线程结束
        System.out.println(m);
    }
} 

4.CountDownLatch

CountDownLatch是Java中自带的锁,它的实现也是基于AQS

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

/**
 * java自身带的锁:CountDownLatch
 */

public class CountDown {

    public static int m = 0;
    public static Lock lock =new Mylock();
    public static CountDownLatch latch = new CountDownLatch(100);//给其一个值,为100,当所有线程结束值为零

    public static void main(String[] args) throws InterruptedException {
        Thread[] threads = new Thread[100];
        for (int i = 0; i < threads.length; i++) {
            threads[i] = new Thread(()->{
               try {
                   lock.lock();
                   for (int j = 0; j < 100; j++) m++;


               }finally {
                   lock.unlock();
               }
                latch.countDown();//每次调用减一
            });
        }
        for (Thread t: threads) t.start();

       latch.await();//等待latch打开,当值变为0时打开,当所有线程执行完后值变为0

       System.out.println(m);
    }

} 

5.使用AQS自定义锁

AQS的使用:

在AQS内部有一个int类型的值:State,在这个数基础之上管理着一个双向队列,当有新的线程去访问这个值的时候,如果没有竞争不到访问权,就在加在这个队列之后

使用Lock接口来写实现类,使用AQS来实现锁

import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

public class Mylock implements Lock{

   private  Sync sync = new Sync();

   @Override
   public void lock() {
      sync.acquire(1);//调用acquire,不是当下写的Tryacquire
   }

   @Override
   public void lockInterruptibly() throws InterruptedException {

   }

   @Override
   public boolean tryLock() {
      return false;
   }

   @Override
   public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
      return false;
   }

   @Override
   public void unlock() {

   }

   @Override
   public Condition newCondition() {
      return null;
   }

   /*类名Sync,继承AbstractQueuedSynchronizer*/
   public class Sync extends AbstractQueuedSynchronizer{
      @Override
      /*重写 tryAcqurie方法 */
      protected boolean tryAcquire(int arg) {
         assert arg == 1; //在JDKAPI文档中,要求要传进来一个1,所以用assert〔〕
         if (compareAndSetState(0,1)) {//这里使用到了CAS
            /*CAS:判断传进去的值,如果是0将其变为1*/
            //判断m的值,如果是零将其变为1
            setExclusiveOwnerThread(Thread.currentThread());//互斥锁
            return true;
         }
         return false;
      }
      @Override
      /*释放*/
      protected boolean tryRelease(int arg) {
         assert arg == 1;
         /*判断当前线程是否持有这把锁*/
         if(!isHeldExclusively()) throw new IllegalMonitorStateException();
         setExclusiveOwnerThread(null);
         setState(0);
         return true;
      }
      @Override
      protected boolean isHeldExclusively() {
         return getExclusiveOwnerThread() == Thread.currentThread();//拿到当前拥有的排它的线程和当前线程是否一样

      }
   }

} 

这里所介绍的锁都是基于AQS来实现.

推荐阅读

字节跳动总结的设计模式 PDF 火了,完整版开放分享

刷Github时发现了一本阿里大神的算法笔记!标星70.5K

月薪没有达到 30K 的Java程序员 是听不懂这个项目的

为什么阿里巴巴的程序员成长速度这么快,看完他们的内部资料我懂了

程序员达到50W年薪所需要具备的知识体系。

关于【暴力递归算法】你所不知道的思路

看完三件事❤️

如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:

点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。

关注公众号 『 Java斗帝 』,不定期分享原创知识。

同时可以期待后续文章ing🚀

点赞
收藏
评论区
推荐文章
Wesley13 Wesley13
3年前
java高并发核心类 AQS(Abstract Queued Synchronizer)抽象队列同步器
什么是AQS?全称:AbstractQueuedSynchronizer:抽象队列同步器是java.util.concurrent.locks包下的一个抽象类其编写者:DougLea(并发大佬,整个j.u.c包都是他写的)是j.u.c包的基础组件(核心)我们先来读一下该类的英文说明注
Wesley13 Wesley13
3年前
Java并发之AQS详解
一、概述  谈到并发,不得不谈ReentrantLock;而谈到ReentrantLock,不得不谈AbstractQueuedSynchronizer(AQS)!  类如其名,抽象的队列式的同步器,AQS定义了一套多线程访问共享资源的同步器框架,许多同步类实现都依赖于它,如常用的ReentrantLock/Semaphore/CountD
Wesley13 Wesley13
3年前
Java高级面试必问:AQS 到底是什么?
前言JDK1.5以前只有synchronized同步锁,并且效率非常低,因此大神DougLea自己写了一套并发框架,这套框架的核心就在于AbstractQueuedSynchronizer类(即AQS),性能非常高,所以被引入JDK包中,即JUC。那么AQS是怎么实现的呢?本篇就是对AQS及其相关组件进行分析,了解其原理,并领略大神的优美而又精简
Stella981 Stella981
3年前
AQS (AbstractQueuedSynchronizer)源码导读:锁的获得与释放
AQS是什么?AbstractQueuedSynchronizer简称AQS是一个抽象同步框架,可以用来实现一个依赖状态的同步器。Providesaframeworkforimplementingblockinglocksandrelatedsynchronizers(semaphores,events,etc)th
Wesley13 Wesley13
3年前
Java的AQS
!(https://oscimg.oschina.net/oscnet/d87ee0244e37f312627f0d76446d7d6efc0.jpg)简介AQS是AbstractQueuedSynchronizer的简称。AQS提供了一种实现阻塞锁和一系列依赖FIFO等待队列的同步器的框架,为一系列同步器依赖于一个单独的原子变量(
Stella981 Stella981
3年前
ReetrantLock源码分析
ReentrantLock类的大部分逻辑,都是其均继承自AQS的内部类Sync实现的啥是AQS:Java并发编程核心在于java.concurrent.util包而juc当中的大多数同步器实现都是围绕着共同的基础行为,比如「等待队列、条件队列、独占获取、共享获取」等,而这个行为的抽象就是基于AbstractQueuedSynchron
Wesley13 Wesley13
3年前
AQS之工作原理
前面一章LZ简单的介绍了下AbstractQueuedSynchronizer(AQS)以及AQS中提供的一些模板方法和作用,这一章LZ将用一个简单的实例来介绍下AQS中独占锁的工作原理。独占锁顾名思义就是在同一时刻只能有一个线程能获取到锁,而其它需要获取这把锁的线程将进入到同步队列中等待获取到了锁的线程释放这把锁,只有获取锁的线程释放了锁,同步队列中的线程
Wesley13 Wesley13
3年前
Java中的AQS到底是什么?高级面试必问!
前言JDK1.5以前只有synchronized同步锁,并且效率非常低,因此大神DougLea自己写了一套并发框架,这套框架的核心就在于AbstractQueuedSynchronizer类(即AQS),性能非常高,所以被引入JDK包中,即JUC。那么AQS是怎么实现的呢?本篇就是对AQS及其相关组件进行分析,了解其原理,并
Wesley13 Wesley13
3年前
AQS 原理剖析
!(https://images.ytao.top/20200627230025.jpg)AQS即AbstractQueuedSynchronizer类称作队列同步器,是构建其他同步器的一个重要的基础框架,同步器自身是没有实现任何同步接口。它是通过控制一个int类型的state变量来表示同步状态,使用一个内置的FIFO(先进先出)
Wesley13 Wesley13
3年前
Java中的队列同步器AQS
一、AQS概念  1、队列同步器是用来构建锁或者其他同步组件的基础框架,使用一个int型变量代表同步状态,通过内置的队列来完成线程的排队工作。  2、下面是JDK8文档中对于AQS的部分介绍  publicabstractclassAbstractQueuedSynchronizerextendsAbstract
Wesley13 Wesley13
3年前
AQS
AQS初探1.AQS(AbstractQueuedSychronizer)简述1.AbstractQueuedSychronizer是java.util.concurrent包下同步器类的灵魂组件,很多同步组件都是基于它实现的,比如CountD