深入理解java并发编程之aqs框架

2024-09-07 23:12

本文主要是介绍深入理解java并发编程之aqs框架,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在这里插入图片描述

跟synchronized 相比较,可重入锁ReentrankLock其实原理有什么不同?

  所得基本原理是为了达到一个目的;就是让所有线程都能看到某种标记。synchronized通过在对象头中设置标记实现了这一目的,是一种JVM原生的锁实现方式。而ReentrantLock以及所有的基于Lock接口的实现类,都是通过一个volitile修饰的int型变量,并保证每个线程都能拥有对该int的可见性和原子性修改,其本质都是基于AQS框架。

那么请谈谈AQS框架是怎么回事儿?

  AQS(AbstractQueuedSynchronizer类)是一个用来构建所和同步器的框架,各种的Lock包中的锁(常用的有 ReentrantLock、ReadWriteLock),以及其他的如Semaphore、CountDownLatch,甚至是早期的FutureTask等等,都是基于AQS来构建。

  • 1、AQS在内部定义了一个volatile int state 变量,表示同步状态;当线程调用lock方法的时候,如果state=0,说明没有任何线程占用共享资源的锁,可以获得锁并将state=1;如果state=1,则说明有线程目前正在使用共享变量,其他线程必须加入同步队列进行等待。
  • 2、AQS通过Node内部类构成一个双向链表结构的同步队列,来完成线程获取锁的排队工作,当线程获取锁失败之后,就被添加到队列末尾。
    Node 类是对要访问同步代码的线程的封装,包含了线程本身及其状态叫waitStatus(有五种不同取值,分别表示是否被阻塞,是否等待唤醒,是否已经被取消等),每个Node结点关联器prev结点和next结点,方便线程释放锁后快速唤醒下一个在等待的线程,是一个FIFO的过程。
  • Node类有两个常量,SHARED和EXCLUSIVE,分别代表共享模式和独占模式。所谓共享模式是一个锁允许多条线程同时操作(信号量 Smaphore 就是基于AQS的共享模式实现的),独占模式是同一个时间段只能有一个线程对共享资源进行操作,多余的请求线程需要排队等待。
  • 3、AQS通过内部类ConditionObject 构建等待队列(可有多个),当Condition 调用wait()方法之后,线程将会加入等待队列中,而当Condition 调用 signal()方法之后,线程将从等待队列转移到同步队列中进行锁竞争。
  • 4、AQS和Condition各自维护了不同的队列,在使用Lock和Condition的时候,其实就是两个队列的互相移动。

请尽可能详细的对比一下synchronized和ReentrantLock的异同

  ReentrantLock 是Lock的实现类,是一个互斥的同步锁。

  从功能的角度上ReentrantLock比synchronized同步操作更加精细,甚至实现synchronized没有高级功能,例如

  • 等待可中断:当持有锁的线程长期不释放锁的时候,正在等待的线程可以选择放弃等待,对处理执行时间非常长的同步块很有用
  • 带超时的获取锁尝试:在指定的时间范围内获取锁,如果时间到了仍然无法获取则返回。
  • 可以判断是否有线程在排队等待获取锁。
  • 可以响应中断请求:与synchronized不同,当获取到锁的线程被中断时,能够响应中断,中断异常将会被抛出,同时锁会被释放。
  • 可以实现公平锁。

  从锁释放酵素synchronized在JVM层面上实现的,不但可以通过一些监控工具监控synchronized的锁定,而且在代码执行出现异常的时候,JVM会自动释放锁定;但是使用Lock则不行,Lock是通过代码实现的,要保证锁定一定会被释放,就必须将unLock()放到finally()代码中。

  从性能角度上来讲,Synchronized早期实现的比较低效,对比ReentrantLock,大多数的使用场景性能都较差。但是Java 6中对其进行了很多的改进,在竞争不激烈的时候,synchronized 的性能要优于 ReentrantLock;在高竞争的情况下,synchronized的性能会下降几十倍,但是ReentrantLock的性能则不变。

ReentrantLock是如何实现可重入性的?

  ReentrantLock内部自定义了同步器Sync(Sync即实现了AQS,又实现了AOS,而AOS提供了一种互斥锁持有的方式)也就是在加锁的时候使用了CAS算法,将线程对象放入到一个双向链表中,每次获取锁的时候,看一下当前维护的是那个线程ID和当前请求的线程ID是否一样,如果一样就可以获取到锁,如果不一样就无法获取到锁。

除了ReentrantLock,还有那些JUC中的并发工具?

  通常所说的并发包(JUC)也就是java.util.concurrent 及其子包,集中了Java并发的各种基础工具类,具体主要包括几个方面:

  • 提供了CountDownLatch、CyclicBarrier、Semaphore等等,这些都比synchronized更加高级,可以实现更加丰富的多线程控制同步结构。
  • 提供了ConcurrentHashMap、有序的ConcurrentSkipListMap、或者通过类似快照机制实现线程安全的动态数组CopyOnWriteArrayList等,各种线程安全的容器。
  • 提供了ArrayBlockingQueue、SynchronousQueue或者针对特定场景的PriorityBlockingQueue等等,各种并发队列实现。
  • 强大的Executor框架,可以创建各种不同类型的线程池,调度任务运行等等。

请谈一谈ReadWriteLock 和 StampedLock的区别。

  虽然ReentrantLock和synchronized使用简单,但是实现行为上有一定的局限性,要么不占,要么独占。实际应用场景中。有时候不需要大量竞争的写操作,而是以并发读取为主,为了进一步优化并发操作的粒度,Java提供了读写锁。

  读写锁基于的原理是多个读操作不需要互斥,如果读锁试图锁定时,写锁是被某个线程持有,读锁将无法获取,而只好等待对方操作结束,这样就可以自动保证不会读取到有争议的数据。

  ReadWriteLock代表了一对锁,下图是一个基于读写锁实现的数据结构,当数据量较大,并发读多,并发写少的时候。能够比纯同步版本凸显出优势。
在这里插入图片描述
  读写锁看起来比synchronized的粒度似乎更细一些,但在实际应用中,其表现也不是太好。主要还是因为相对比较大的开销。

  所以JDK在后期又引入了StampedLock,在提供类似读写锁的同时,还支持优化读模式,优化读基于假设,大多数情况下读操作并不会和写操作冲突,其逻辑是先试着修改,然后通过validate方法确认是否进行写模式,如果没有进入,就成功避免了开销;如果进入,则尝试获取读锁。
在这里插入图片描述

如何让Java线程彼此同步?了解过那些同步器?请分别介绍下。

  JUC中的同步器三个主要成员:CountDownLatch、CyclicBarrier和Semaphore,通过它们可以方便的实现很多线程之间的协助功能。CountDownLatch叫倒计数,允许一个或者多个线程等待某些操作完成。

  • 跑步比赛,裁判需要等到所有运动员都跑到终点才能计算成绩。
  • 模拟并发,需要启动100个线程同时访问某个地址,希望他们能并发执行而不是一个一个执行

  用法:CountDownLatch 构造方法指明计数数量,被等待线程调用countDown 将计数器减1,等待线程使用await进行线程等待。
在这里插入图片描述
  CyclicBarrier 叫做循环栅栏,它实现让一组线程等待至某个状态之后再全部同时执行,而且当所有等待线程被释放之后,CyclicBarrier可以被重复使用。CyclicBarrier比较典型的应用场景是用来等待并发线程结束。

  主要方法是await(),await()每被调用一次,计数则会减少1,并且阻塞当前线程。当计数器减至0 的时候,阻塞解除,所有在此CyclicBarrier上阻塞的线程开始运行。

  这之后,如果再次调用await(),计数就会变成n-1,新一轮重新开始,这便是Cyclic的含义,CyclicBarrier.await()带有返回值。用来表示当前线程是第一个到达这个Barrier的线程

在这里插入图片描述
  Semaphore ,Java版本的信号量实现,用于控制同时访问的线程个数,来达到限制通用资源访问的目的,其原理通过acquire()获取一个许可,如果没有就等待,而release()释放一个许可。

在这里插入图片描述
  如果、Semaphore 的数值被初始化为1,那么一个线程就可以通过acquire进入互斥状态,本质上和互斥锁是非常相似的。但是区别也非常明显,比如互斥锁是持有者的,而对于Semaphore 这种计数器结构,虽然有类似功能,但其实不存在真正意义上的持有者,除非进行扩展包装。

CyclicBarrier 和CountDownLatch 看起来很相似,对比一下?

  首先它们的行为有相似度,主要区别如下

  • CountDownLatch 是不可以重置的,所以无法重用,CyclicBarrier没有这种限制,可以重用
  • CountDownLatch 的基本操作组合是countDown/await,调用await的线程阻塞等待countDown足够的次数,不管是一个线程还是多个线程countDown,只要次数足够就可以。CyclicBarrier的基本操作组合是await,当所有的伙伴都调用了await(),才会执行任务,并且自动重置。
  • CountDownLatch目的是让一个线程等待其他N个线程达到某个时间后自己再去做某个事情(通过CyclicBarrier 第二个构造方法 public CyclicBarrier(int parties,Runnable barrierAction),在新线程里做事可以达到同样的效果),而CyclicBarrier 的目的是让N多个线程相互等待知道所有的线程都打到某个状态,然后这N个线程再去执行后续的事情。(通过CountdownLatch在某些场合也能完成类似的效果)

这篇关于深入理解java并发编程之aqs框架的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1146436

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory

认识、理解、分类——acm之搜索

普通搜索方法有两种:1、广度优先搜索;2、深度优先搜索; 更多搜索方法: 3、双向广度优先搜索; 4、启发式搜索(包括A*算法等); 搜索通常会用到的知识点:状态压缩(位压缩,利用hash思想压缩)。