Java学习-JUC

目录

1. 简介

2. Atomic包

2.1 什么是原子类

2.2 Atomic包里的类

3. CAS

3.1 CAS是什么

3.2 Java中对CAS的实现

3.3 CAS的缺陷

4. JUC里面的常见锁

4.1 锁分类

4.1.1 按上锁方式划分

4.1.2 按特性划分

4.1.3 其他锁

4.2 Synchronized和JUC的锁对比

5. 锁原理分析

5.1 AQS

5.2 ReentrantLock源码分析-锁的获取

5.3 ReentrantLock源码分析-锁的释放

5.4 公平锁和非公平锁源码实现区别

5.5 读写锁ReentrantReadWriteLock

5.6 锁优化

6. 线程协作工具类

6.1 CountDownLatch计数门闩

6.2 Semaphore信号量

6.3 CyclicBarrier循环栅栏

6.4 Condition接口

7. 并发容器

7.1 什么是并发容器

7.2 常见并发容器特点总结

7.3 ConcurrentHashMap

7.4 CopyOnWriteArrayList

8. 并发队列

8.1 为什么要用队列

8.2 什么是阻塞队列

8.3 常用阻塞队列

1. 简介

从JDK1.5起,Java API 中提供了java.util.concurrent(简称JUC)包,在此包中定义了并发 编程中很常用的工具。
JUC是 JSR 166 标准规范的一个实现,JSR 166 以及 JUC 包的作者是同一个人 Doug Lea 。

2. Atomic包

2.1 什么是原子类

JDK1.5之后,JUC的atomic包中,提供了一系列用法简单、性能高效、线程安全的更新一个变量的类,这些称之为原子类。

作用:保证共享变量操作的原子性、可见性,可以解决volatile原子性操作变量的BUG

2.2 Atomic包里的类

➢基本类型:AtomicInteger整形原子类…
➢引用类型:AtomicReference引用类型原子类…
➢数组类型:AtomicIntegerArray整形数组原子类…
➢对象属性修改类型:AtomicIntegerFieldUpdater原子更新整形字段的更新器…➢JDK1.8新增:DoubleAdder双浮点型原子类、LongAdder长整型原子类…

虽然原子类很多,但原理几乎都差不多,其核心是采用CAS进行原子操作

3. CAS

3.1 CAS是什么

CAS即compare and swap(比较再替换),同步组件中大量使用CAS技术实现了Java多线程的并发操作。整个AQS、Atomic原子类底层操作,都可以看见CAS。甚至ConcurrentHashMap在1.8的版本中也调整为了CAS+Synchronized。可以说CAS是整个JUC的基石。

CAS本质是一条CPU的原子指令,可以保证共享变量修改的原子性

其实,CAS本不难,它只是一个方法而已,这个方法长这样:执行函数:CAS(V,E,N)
➢V:要读写的内存地址
➢E:进行比较的值(预期值)
➢N:拟写入的新值
➢当且仅当内存地址的V 中的值等于预期值E 时,将内存地址的V中的值改为N,否则会进行自旋操作,即不断的重试。

jvm

3.2 Java中对CAS的实现

jvm

jvm

3.3 CAS的缺陷

CAS虽然很好的解决了共享变量的原子操作问题,但还是有一些缺陷:
➢循环时间不可控:如果CAS一直不成功,那么CAS自旋就是个死循环。会给CPU造成负担 ReentrantReadWriteLock读写锁:它维护了一对锁,ReadLock读锁和WriteLock写锁。读写锁适合读多写少的场景基本原则:读锁可以被多个线程同时持有进行访问,而写锁只能被一个线程持有。可以这 么理解:读写锁是个混合体,它既是一个共享锁,也是一个独享锁。 ➢StampedLock重入读写锁,JDK1.8引入的锁类型,是对读写锁ReentrantReadWriteLock的增强版。 ➢只能保证一个共享变量原子操作
➢ABA问题:CAS检查操作的值有没有发生改变,如果没有则更新。这就存在一种情况:如果原来的值是A,然后变成了B,然后又变为A了,那么CAS检测不到数据发生了变化,但是其实数据已经改变了。

4. JUC里面的常见锁

JUC包提供了种类丰富的锁,每种锁特性各不相同
➢ReentrantLock重入锁:它具有与使用synchronized 相同的一些基本行为和语义,但是它的API功能更强大,重入锁相当于synchronized 的增强版,具有synchronized很多所没有的功能。它是一种独享锁(互斥锁),可以是公平锁,也可以是非公平的锁。

➢ReentrantReadWriteLock读写锁:它维护了一对锁,ReadLock读锁和WriteLock写锁。读写锁适合读多写少的场景。基本原则:读锁可以被多个线程同时持有进行访问,而写锁只能被一个线程持有。可以这么理解:读写锁是个混合体,它既是一个共享锁,也是一个独享锁。

➢StampedLock重入读写锁,JDK1.8引入的锁类型,是对读写锁ReentrantReadWriteLock的增强版。

4.1 锁分类

4.1.1 按上锁方式划分

①隐式锁:synchronized,不需要显示加锁和解锁
②显式锁:JUC包中提供的锁,需要显示加锁和解锁

4.1.2 按特性划分

悲观锁/乐观锁:按照线程在使用共享资源时,要不要锁住同步资源,划分为悲观锁和乐观锁

  • 悲观锁:JUC锁,synchronized
  • 乐观锁:CAS,关系型数据库的版本号机制

重入锁/不可重入锁:按照同一个线程是否可以重复获取同一把锁,划分为重入锁和不可重入锁

  • 重入锁:ReentrantLock、synchronized
  • 不可重入锁:不可重入锁,与可重入锁相反,线程获取锁之后不可重复获取锁,重复获取会发生死锁

公平锁/非公平锁:按照多个线程竞争同一锁时需不需要排队,能不能插队,划分为公平锁和非公平锁。

  • 公平锁:new ReentrantLock(true)多个线程按照申请锁的顺序获取锁
  • 非公平锁:new ReentrantLock(false)多个线程获取锁的顺序不是按照申请锁的顺序(可以插队) synchronized

独享锁/共享锁:按照多个线程能不能同时共享同一个锁,锁被划分为独享锁和共享锁

  • 独享锁:独享锁也叫排他锁,synchronized,ReentrantLock,ReentrantReadWriteLock的WriteLock写锁
  • 共享锁:ReentrantReadWriteLock的ReadLock读锁

4.1.3 其他锁

自旋锁:

  • 实现:CAS、轻量级锁

分段锁:

  • 实现:ConcurrentHashMap ConcurrentHashMap所使用的锁分段技术,首先将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。

无锁/偏向锁/轻量级锁/重量级锁

  • 这四个锁是synchronized独有的四种状态,级别从低到高依次是:无锁、偏向锁、轻量级锁和重量级锁。
  • 它们是JVM为了提高synchronized锁的获取与释放效率而做的优化
  • 四种状态会随着竞争的情况逐渐升级,而且是不可逆的过程,即不可降级

4.2 Synchronized和JUC的锁对比

Synchronize的缺陷:
➢ 第一: Synchronized无法控制阻塞时长,阻塞不可中断
◼ 使用Synchronized,假如占有锁的线程被长时间阻塞(IO、sleep、join),由于线程阻塞时没法释放锁,会导致大 量线程堆积,轻则影响性能,重则服务雪崩
◼ JUC的锁可以解决这两个缺陷
➢ 第二:读多写少的场景中,多个读线程同时操作共享资源时不需要加锁
◼ Synchronized不论是读还是写,均需要同步操作,这种做法并不是最优解
◼ JUC的ReentrantReadWriteLock锁可以解决这个问题

5. 锁原理分析

在重入锁ReentrantLock类关系图中,可以看到NonfairSync和FairSync都继承自抽象类Sync,而Sync类继 承自抽象类AbstractQueuedSynchronizer(简称AQS)。

jvm

5.1 AQS

AQS即队列同步器,是JUC并发包中的核心基础组件,其本身只是一个抽象类。其实现原理与前面介绍的 Monitor管程是一样的,AQS中也用到了CAS和Volatile。

由类图可以看到,AQS是一个FIFO的双向队列,队列中存储的是thread,其内部通过节点head和tail记录队首 和队尾元素,队列元素的类型为Node

jvm

AQS中的内部静态类Node为链表节点,AQS会在线程获取锁失败后,线程会被阻塞并被封装成Node加入到 AQS队列中;当获取锁的线程释放锁后,会从AQS队列中的唤醒一个线程(节点)。

jvm

  • 线程抢夺锁失败时,AQS队列的变化【加锁】

① AQS的head、tail分别代表同步队列头节点和尾节点指针默认为null
② 当第一个线程抢夺锁失败,同步队列会先初始化,随后线程会被封装成Node节点追加到AQS队列中。
➢ 假设:当前独占锁的的线程为ThreadA,抢占锁失败的线程为ThreadB。
➢ 2.1 同步队列初始化,首先在队列中添加Node,thread=null
➢ 2.2 将ThreadB封装成为Node,追加到AQS队列
③ 当下一个线程抢夺锁失败时,继续重复上面步骤。假设:ThreadC抢占线程失败

jvm

  • 线程被唤醒时,AQS队列的变化【解锁】

① ReentrantLock唤醒阻塞线程时,会按照FIFO的原则从AQS中head头部开始唤醒首个节点中线程。
② head节点表示当前获取锁成功的线程ThreadA节点。
③ 当ThreadA释放锁时,它会唤醒后继节点线程ThreadB,ThreadB开始尝试获得锁,如果ThreadB获得锁成功,会将自 己设置为AQS的头节点。ThreadB获取锁成功后,AQS变化如下:

jvm

5.2 ReentrantLock源码分析-锁的获取

ReentrantLock锁获取源码分析:

jvm

5.3 ReentrantLock源码分析-锁的释放

ReentrantLock锁释放源码分析

jvm

5.4 公平锁和非公平锁源码实现区别

公平锁/非公平锁:按照多个线程竞争同一锁时需不需要排队,能不能插队
获取锁的两处差异:
① lock方法差异
② tryAcquire差异

jvm

jvm

5.5 读写锁ReentrantReadWriteLock

读写锁:维护着一对锁(读锁和写锁),通过分离读锁和写锁,使得并发能力比一般的互斥锁有较大 提升。同一时间,可以允许多个读线程同时访问,但在写线程访问时,所有读写线程都会阻塞。 所以说,读锁是共享的,写锁是排他的。

主要特性:
➢ 支持公平和非公平锁
➢ 支持重入
➢ 锁降级:写锁可以降级为读锁,但是读锁不能升级为写锁

jvm

5.6 锁优化

如何优化锁?

➢ 减少锁的持有时间
➢ 减少锁粒度
◆ 将大对象拆分为小对象,增加并行度,降低锁的竞争
◆ 例如:早期ConcurrentHashMap的分段锁
➢ 锁分离
◆ 根据功能场景进行锁分离
◆ 例如:读多写少的场景,使用读写锁可以提高性能
➢ 锁消除:锁消除是编译器自动的一种优化方式
➢ 锁粗化
◆ 增加锁的范围,降低加解锁的频次

6. 线程协作工具类

6.1 CountDownLatch计数门闩

◆ 倒数结束之前,一直处于等待状态,直到数到0,等待线程才继续工作。
◆ 场景:购物拼团、分布式锁
◆ 方法:
① new CountDownLatch(int count)
② await():调用此方法的线程会阻塞,支持多个线程调用,当计数为0,则唤醒线程
③ countdown():其他线程调用此方法,计数减1

6.2 Semaphore信号量

◆ 限制和管理数量有限的资源的使用
◆ 场景:Hystrix、Sentinel限流
◆ 方法:
① new Semaphore ((int permits) 可以创建公平的非公平的策略
② acquire():获取许可证,获取许可证,要么获取成功,信号量减1,要么阻塞等待唤醒
③ release():释放许可证,信号量加1,然后唤醒等待的线程

6.3 CyclicBarrier循环栅栏

◆ 线程会等待,直到线程到了事先规定的数目,然后触发执行条件进行下一步动作
◆ 场景:并行计算
◆ 方法:
① new CyclicBarrier(int parties, Runnable barrierAction)参数1集结线程数,参数2凑齐之后执行的任务
② await():阻塞当前线程,待凑齐线程数量之后继续执行

6.4 Condition接口

◆ 控制线程的“等待”和“唤醒”
◆ 方法:
① await():阻塞线程
② signal():唤醒被阻塞的线程
③ signalAll()会唤起所有正在等待的线程。
◆ 注意:
① 调用await()方法时必须持有锁,否则会抛出异常
② Condition和Object#await/notify方法用法一样,两者await方法都会释放锁

jvm

7. 并发容器

7.1 什么是并发容器

针对多线程并发访问来进行设计的集合,称为并发容器
➢ JDK1.5之前,JDK提供了线程安全的集合都是同步容器,线程安全,只能串行执行,性能很差。
➢ JDK1.5之后,JUC并发包提供了很多并发容器,优化性能,替代同步容器

jvm

什么是同步容器?线程安全的集合与非安全集合有什么关系?

每次只有一个线程可以访问的集合(同步),称为线程安全的集合,也叫同步容器
➢ Java集合主要为4类:List、Map、Set、Queue,线程不安全的:ArrayList、HashMap..
➢ JDK早期线程安全的集合Vector、Stack、HashTable。
➢ JDK1.2中,还为Collections增加内部Synchronized类创建出线程安全的集合,实现原理synchronized

7.2 常见并发容器特点总结

➢ List容器
① Vector:synchronized实现的同步容器,性能差,适合于对数据有强一致性要求的场景
② CopyOnWriteArrayList :底层数组实现,使用复制副本进行有锁写操作(数据不一致问题),适合读多写少,允 许短暂的数据不一致的场景
➢ Map容器
① Hashtable : synchronized实现的同步容器,性能差,适合于对数据有强一致性要求的场景
② ConcurrentHashMap :底层数组+链表+红黑树(JDK1.8)实现,对table数组entry加锁( synchronized ), 存在一致性问题。适合存储数据量小,读多写少,允许短暂的数据不一致的场景
③ ConcurrentSkipListMap :底层跳表实现,使用CAS实现无锁读写操作。适合与存储数据量大,读写频繁,允许短 暂的数据不一致的场景
➢ Set容器
① CopyOnWriteArraySet :底层数组实现的无序Set
② ConcurrentSkipListSet :底层基于跳表实现的有序Set

7.3 ConcurrentHashMap

JDK1.7结构图

jvm

JDK1.8结构图
➢ 底层采用数组+链表+红黑树数据结构
➢ 存入key值,使用hashCode映射数组索引
➢ 集合会自动扩容:加载因子0.75f
➢ 链表长度超过8时,链表转换为红黑树

jvm

7.4 CopyOnWriteArrayList

CopyOnWriteArrayList底层数组实现,使用复制副本进行有锁写操作,适合读多写少,允许短 暂的数据不一致的场景。
CopyOnWrite思想:平时查询时,不加锁,更新时从原来的数据copy副本,然后修改副本,最后把原数据 替换为副本。修改时,不阻塞读操作,读到的是旧数据

优缺点
➢ 优点:对于读多写少的场景, CopyOnWrite这种无锁操作性能更好,相比于其它同步容器
➢ 缺点:①数据一致性问题,②内存占用问题及导致更多的GC次数

8. 并发队列

8.1 为什么要用队列

队列是线程协作的利器,通过队列可以很容易的实现数据共享,并且解决上下游处理速度不匹配的问题,典型的生 产者消费者模式

8.2 什么是阻塞队列

➢ 带阻塞能力的队列,阻塞队列一端是给生产者put数据使用,另一端给消费者take数据使用
➢ 阻塞队列是线程安全的,生产者和消费者都可以是多线程
➢ take方法:获取并移除头元素,如果队列无数据,则阻塞
➢ put方法:插入元素,如果队列已满,则阻塞
➢ 阻塞队列又分为有界和无界队列,无界队列不是无限队列,最大值Integer.MAX_VALUE

8.3 常用阻塞队列

  1. ArrayBlockingQueue 基于数组实现的有界阻塞队列
  2. LinkedBlockingQueue 基于链表实现的无界阻塞队列
  3. SynchronousQueue不存储元素的阻塞队列
  4. PriorityBlockingQueue 支持按优先级排序的无界阻塞队列
  5. DelayQueue优先级队列实现的双向无界阻塞队列
  6. LinkedTransferQueue基于链表实现的无界阻塞队列
  7. LinkedBlockingDeque基于链表实现的双向无界阻塞队列

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/450460.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

智慧园区能带来哪些便利?

所谓智慧园区,是指通过信息化手段,实现园区内各项业务的数字化和智能化管理。园区管理者可以利用智能化平台实时监控各项运营情况,如能源使用、安全监控和物流运输等,及时调整管理策略,提高运营效率。智慧园区利用大数…

pycharm 找不到conda环境

参考:新版Pycharm解决Conda executable is not found-CSDN博客

WNMP环境本地搭建并配置公网地址远程搭建动态网站或服务器

文章目录 前言1.Wnmp下载安装2.Wnmp设置3.安装cpolar内网穿透3.1 注册账号3.2 下载cpolar客户端3.3 登录cpolar web ui管理界面3.4 创建公网地址 4.固定公网地址访问 前言 本教程主要介绍如何在Windows系统电脑本地下载安装 Wnmp,以及结合cpolar内网穿透工具一键配…

【C++】——多态(上)

【C】——多态(上) 1 多态的概念2 多态的定义及实现2.1 多态的构成条件2.1.1 实现多态的必要条件2.1.2 虚函数2.1.3 感受多态2.1.4 判断是否满足多态2.1.5 多态场景的一道选择题2.1.6 虚函数重写的一些其他问题2.1.6.1 协变2.1.6.2 析构函数的重写 2.1.7…

深度学习-26-基于PyTorch的多层感知机DNN

文章目录 1 代码分析1.1 加载数据集1.2 定义模型1.3 定义损失函数和优化器1.4 定义训练函数1.4.1 定义累加器Accumulator1.4.2 计算准确率accuracy1.4.3 评估函数evaluate_accuracy1.4.4 单轮训练函数train_epoch1.4.5 训练函数train1.2 执行训练2 整体代码3 参考附录1 代码分析…

使用Python构建一个高级计算器

使用Python构建一个高级计算器 在现代编程中,构建一个功能强大的计算器不仅是学习编程的好项目,还有助于提高对数据处理和用户交互的理解。本文将带您通过使用Python构建一个高级计算器,该计算器支持基本运算、科学运算以及简单的图形用户界…

将SpringBoot项目部署到linux服务器使得本地可以访问

首先我们要先从本地打包jar文件上传到linux中,这些的步骤可以参考其他打包上传的博客哈 打包上传后,可以看到对应的 .jar 文件 如果这样直接运行java -jar code-sandbox-0.0.1-SNAPSHOT.jar 是不行的,因为你还没有在服务器上开放端口&#x…

ubuntu中使用cmake编译报错No CMAKE_CXX_COMPILER could be found.的解决方法

ubuntu中使用cmake编译报错No CMAKE_CXX_COMPILER could be found.的解决方法 No CMAKE_CXX_COMPILER could be found.Could NOT find CUDA (missing: CUDA_NVCC_EXECUTABLE CUDA_CUDART_LIBRARY)Could not find a package configuration file provided by "OpenCV" …

中国制造业精益生产管理的现状与挑战

在当今全球制造业竞争日益激烈的背景下,精益生产管理作为一种高效、灵活的生产模式,已成为众多企业追求的核心竞争力之一。然而,尽管精益生产理念在中国制造业中已得到广泛传播和应用,其实践水平却参差不齐,多数企业仍…

Ansible概述

目录 一、ansible简介 二、absible的特点 三、ansible的工作原理以及流程 四、ansible环境安装部署 五、ansible命令行模块 六、inventory 主机清单 一、ansible简介 Ansible是一个基于Python开发的配置管理和应用部署工具,现在也在自动化管理领域大放异彩。…

解决ultralytics中的YOLOv8在执行task.py文件添加模块操作出现的KeyError报错

报错详情: 在ultralytics项目文件夹内运行/home/xxx/ultralytics/train.py进行单GPU训练的时候训练可以正常进行 from ultralytics import YOLO# Load a model model YOLO("/home/xxx/ultralytics/ultralytics/cfg/models/v8/yolov8s-FASFF.yaml") # …

3.C++经典实例-计算一个数的阶乘

阶乘(factorial)是‌基斯顿卡曼于1808年发明的运算符号,用于表示一个正整数n的所有小于及等于该数的正整数的积。自然数n的阶乘写作n!。例如,5的阶乘表示为5! 1 2 3 4 5 120。 阶乘在数学和计算机科学中有广泛的应用。例如…

git--git reset

HEAD 单独一个HEAD eg:git diff HEAD 表示当前结点。 HEAD~ HEAD~只处理当前分支。 注意:master分支的上一个结点是tmp分支的所在的结点fc11b74, 79f109e才是master的第二个父节点。 HEAD~ 当前结点的父节点。 HEAD~1 当前结点的父节点。 HEAD~n 当前结点索…

react+video:限制快进、倍速、画中画

实现代码&#xff1a; <video ref{videoRef} src{videoUrl} className{style.video} controls onRateChange{rateChange} onPlay{playVideo} onPause{pauseVideo} onTimeUpdate{timeUpdate} disablePictureInPicture playsInline poster{poster} controlsList"nodownl…

Qml-Item的构造和显示顺序

Qml-Item的构造和显示顺序 qml文件中组件构造顺序 在同一个qml文件中&#xff0c;同层级的Item, 文件尾的Item优先构造&#xff0c;文件首的Item后构造。这就能解释默认情况下同一个qml文件中&#xff0c;几个同层级的item都设置了focus:true&#xff0c;为啥最上面item最终有…

毕业设计选题:基于django+vue的个人博客系统设计与开发

开发语言&#xff1a;Python框架&#xff1a;djangoPython版本&#xff1a;python3.7.7数据库&#xff1a;mysql 5.7数据库工具&#xff1a;Navicat11开发软件&#xff1a;PyCharm 系统展示 管理员登录 管理员功能界面 博主管理 博客文章管理 博文排行管理 博文打赏管理 博文…

设计模式之组合模式(Composite)

一、组合模式介绍 组合模式(Composite Pattern) 的定义是&#xff1a;将对象组合成树形结构以表示整个部分的层 次结构。组合模式可以让用户统一对待单个对象和对象的组合。 如在windows操作系统中的目录结构&#xff0c;其实就是树形目录结构&#xff0c;可以通过 tree /f 命令…

加速“人工智能+”落地,青云如何打磨智算基石

智算中心建设不断加速&#xff0c;正成为推动数字经济发展的新引擎。 根据天风证券的研究&#xff0c;2024年1-6月&#xff0c;全国智算中心招投标相关事件791起&#xff0c;较上年同期增加407.1%。 围绕AI算力&#xff0c;云计算厂商也在积极探索第二增长曲线。根据2024年半年…

2024.10月17日- Vue.js(2)

2.4 计算属性 从字符串反转中&#xff0c;我们发现 插值语法的初衷是用于简单运算。明显练习题中的写法&#xff0c;违背了插值语法的初衷。 methods方法可以。但是方法中如果封装的是性能开销比较大的逻辑代码&#xff0c;需要进行大量的运算&#xff0c;并且别的属性还依赖…

【数据结构与算法】栈和队列

文章目录 一.栈1.1定义 顺序栈和链式栈1.2基本操作1.2.1表示1.2.2初始化1.2.3清空1.2.4销毁1.2.5入栈1.2.6出栈1.2.7取栈顶 1.3共享栈1.3.1定义1.3.2进栈出栈 二.队列2.1定义 顺序队列和链式队列循环队列2.2基本操作2.2.1初始化2.2.2判空2.2.3求队列长度2.2.4取队头元素2.2.5销…