【JavaEE】JUC(Java.util.concurrent)常见类
文章目录
- 前言
- ReentrantLock
- 原子类
- 线程池
- 信号量
- CountDownLatch
- 相关面试题
前言
经过前面文章的学习我们大致了解了如何实现多线程编程和解决多线程编程中遇到的线程不安全问题,java.util.concurrent
是我们多线程编程的一个常用包,那么今天我将为大家分享 java.util.concurrent
包下的其他几种常见的类。
ReentrantLock
ReentrantLock 是可重入互斥锁,跟 synchronized 定位是类似的,都是用来保证线程安全的,但是 ReentrantLock 在某些方面相较于 synchronized 有突出的优势。
ReentrantLock 的加锁方式分为两种:
- lock():如果获取不到锁,线程就会进入阻塞等待状态
- tryLock():如果获取不到锁,就会放弃加锁,而不是进入阻塞等待状态
使用 ReentrantLock 的时候需要手动 unLock() 解锁,如果忘记了手动解锁这个操作,将会带来比较严重的后果。
所以为了解决有些时候忘记手动解锁的情况,往往需要借助 try-finally 来进行解锁操作。
ReentrantLock lock = new ReentrantLock();
lock.lock();
try {//working
}finally {lock.unlock();
}
ReentranLock 与 synchronized 相比的优势
-
ReentrantLock 在加锁的时候,有两种方式,lock() 和tryLock()。使用 lock() 的时候,如果未获取到锁就会进入阻塞等待状态,而使用 tryLock() 的话,如果没有获取到锁就会放弃获取,而不是进入阻塞等待
-
ReentrantLock 提供了公平锁的实现(默认情况下是非公平锁)
-
ReentranLock 提供了更强大的等待通知机制,搭配了Condition类,实现等待通知,可以指定唤醒某个线程
如何选择使用哪个锁?
- 锁竞争不激烈的时候, 使用 synchronized, 效率更高, 自动释放更方便.
- 锁竞争激烈的时候, 使用 ReentrantLock, 搭配 trylock 更灵活控制加锁的行为, 而不是死等.
- 如果需要使用公平锁, 使用 ReentrantLock.
通常情况下还是建议使用 synchronized,虽然 ReentrantLock 在某些方面具有优势,但是使用起来较麻烦,并且Java程序员在 synchronized 里面做了很多的优化。
原子类
原子类内部都是使用 CAS 操作实现的,因为 CAS 操作时原子性的,不需要进行加锁操作,所以性能要比加锁好很多。
- AtomicBoolean
- AtomicInteger
- AtomicIntegerArray
- AtomicLong
- AtomicReference
- AtomicStampedReference
原子类常见方法:
- addAndGet(int delta); —— i += delta;
- decrementAndGet(); —— --i;
- getAndDecrement(); —— i–;
- incrementAndGet(); —— ++i;
- getAndIncrement(); —— i++;
CAS 操作我在前面详细讲解过,大家想要了解的话可以去看看。CAS(Compare And Swap)操作
线程池
线程池是为了解决因线程频繁创建和销毁而造成的资源浪费问题。
使用线程池需要使用到 ExecutorService
和 Executors
两个类。
ExecutorService pool = Executors.newFixedThreadPool(10);
pool.submit(new Runnable() {@Overridepublic void run() {System.out.println("hello");}
});
- ExecutorService 表示一个线程池实例.
- Executors 是一个工厂类, 能够创建出几种不同风格的线程池.
- ExecutorService 的 submit 方法能够向线程池中提交若干个任务
Executors 创建线程池的几种方式
- newFixedThreadPool: 创建固定线程数的线程池
- newCachedThreadPool: 创建线程数目动态增长的线程池.
- newSingleThreadExecutor: 创建只包含单个线程的线程池.
- newScheduledThreadPool: 设定 延迟时间后执行命令,或者定期执行命令. 是进阶版的 Timer。
这里面也涉及到了工厂模式,大家可以去看看这篇文章了解一下【JavaEE】多线程案例-线程池
信号量
信号量(Semaphore)是一种实现任务间通信的机制,可以用于实现任务之间的同步或临界资源的互斥访问。它通常被用于协助一组相互竞争的任务来访问临界资源。
信号量是一个非负整数,获取信号量的任务会将该整数减1。当信号量为0时,所有试图获取该信号量的任务都将处于阻塞状态。信号量的值代表积累下来的释放信号量操作的次数。
申请资源的操作被称为 P 操作,释放资源的操作被称为 V 操作。
信号量可以分为二值信号量和计数信号量两种。二值信号量只有一个消息队列,队列有两种状态:空或满。而计数信号量可以看做长度大于1的消息队列,用于计数。信号量的计数值表示还有多少个事件未被处理。当某个事件发生时,任务或中断会释放一个信号量(将信号量计数值加1);当需要处理某个事件时,任务或中断会取走一个信号量(将信号量计数值减1)。
加锁和解锁的操作就可以看成是二值信号量的操作,当加锁的时候信号量就为0,释放锁的时候信号量就为1.
在Java代码中,信号量相关的操作被封装在 Semaphore
类中,acquire()
方法表示申请资源,release()
方法表示释放资源。
public class Test2 {public static void main(String[] args) throws InterruptedException {Semaphore semaphore = new Semaphore(4);semaphore.acquire();System.out.println("获取资源");semaphore.acquire();System.out.println("获取资源");semaphore.acquire();System.out.println("获取资源");semaphore.acquire();System.out.println("获取资源");semaphore.acquire();System.out.println("获取资源");semaphore.release();System.out.println("释放资源");}
}
当申请的资源量大于总的资源量的时候,线程就会进入阻塞等待状态,直到其他线程释放掉部分信号量。
CountDownLatch
CountDownLatch是Java中的一个同步工具类,用来协调多个线程之间的同步。它允许一个或多个线程等待,直到在其他线程中执行的一组操作完成。
CountDownLatch通过一个计数器来实现这个功能,计数器的初始值通常设置为需要等待完成的线程数量。每当一个线程完成了自己的任务,计数器的值就会减1。当计数器的值达到0时,表示所有线程都已经完成任务,此时在CountDownLatch上等待的线程就可以恢复执行。
CountDownLatch可以用来确保某些活动在其他活动完成之前不会继续执行。例如,可以确保某个计算在其需要的所有资源都被初始化之后才继续执行,或者确保某个服务在其依赖的所有其他服务都已经启动之后才启动。
在Java中,可以使用CountDownLatch的countDown()方法来对计数器做减操作,就是告诉CountDownLatch我这个当前的任务完成了,使用await()方法等待计数器达到0。所有调用await()方法的线程都会被阻塞,直到计数器达到0或者等待线程被中断或者超时。
public class Demo2 {private static int count;public static void main(String[] args) throws InterruptedException {CountDownLatch countDownLatch = new CountDownLatch(10);for(int i = 0; i < 10; i++) {Thread t = new Thread(() -> {try {Thread.sleep((long)(Math.random() * 4000));System.out.println(++count + "号完成比赛");countDownLatch.countDown();} catch (InterruptedException e) {throw new RuntimeException(e);}});t.start();}countDownLatch.await();System.out.println("结束比赛");}
}
只有调用了10次 countDown()
方法之后,await()
方法才会结束等待,继续执行后面的代码。
需要注意的是,CountDownLatch是一次性的,一旦计数器的值达到0,就不能再次使用。如果需要多次使用类似的功能,可以考虑使用CyclicBarrier等其他同步工具类。
相关面试题
1) 线程同步的方式有哪些?
synchronized, ReentrantLock, Semaphore 等都可以用于线程同步.
2) 为什么有了 synchronized 还需要 juc 下的 lock?
以 juc 的 ReentrantLock 为例,
- synchronized 使用时不需要手动释放锁. ReentrantLock 使用时需要手动释放. 使用起来更灵活,
- synchronized 在申请锁失败时, 会死等. ReentrantLock 可以通过 trylock 的方式等待一段时间就放弃.
- synchronized 是非公平锁, ReentrantLock 默认是非公平锁. 可以通过构造方法传入一个 true 开启公平锁模式.
- synchronized 是通过 Object 的 wait / notify 实现等待-唤醒. 每次唤醒的是一个随机等待的线程. ReentrantLock 搭配 Condition 类实现等待-唤醒, 可以更精确控制唤醒某个指定的线程.
3) AtomicInteger 的实现原理是什么?
基于 CAS 机制. 伪代码如下:
class AtomicInteger {private int value;public int getAndIncrement() {int oldValue = value;while ( CAS(value, oldValue, oldValue+1) != true) {oldValue = value;}return oldValue;}
}
4) 信号量听说过么?之前都用在过哪些场景下?
信号量, 用来表示 “可用资源的个数”. 本质上就是一个计数器.
使用信号量可以实现 “共享锁”, 比如某个资源允许 3 个线程同时使用, 那么就可以使用 P 操作作为加锁, V 操作作为解锁, 前三个线程的 P 操作都能顺利返回, 后续线程再进行 P 操作就会阻塞等待,直到前面的线程执行了 V 操作
相关文章:

【JavaEE】JUC(Java.util.concurrent)常见类
文章目录 前言ReentrantLock原子类线程池信号量CountDownLatch相关面试题 前言 经过前面文章的学习我们大致了解了如何实现多线程编程和解决多线程编程中遇到的线程不安全问题,java.util.concurrent 是我们多线程编程的一个常用包,那么今天我将为大家分…...

清除浮动的方法
为什么需要清除浮动? 父级的盒子不能把height定死这样,浮动子类就没有了(行内块元素的特点),父类高度为零。故引用清除浮动 1、父级没有高度 2、子盒子浮动了 3、影响下面的布局了,我们就应该清除浮动了…...

LangChain 摘要 和问答示例
在Azure上的OpenAI端点 注意 OpenAI key 可以用微软 用例【1. 嵌入 ,2. 问答】 1. import os import openai from langchain.embeddings import OpenAIEmbeddings os.environ["OPENAI_API_KEY"] "****" # Azure 的密钥 os.environ["OP…...

(32)测距仪(声纳、激光雷达、深度摄影机)
文章目录 前言 32.1 单向测距仪 32.2 全向性近距离测距仪 32.3 基于视觉的传感器 前言 旋翼飞机/固定翼/无人车支持多种不同的测距仪,包括激光雷达(使用激光或红外线光束进行距离测量)、360 度激光雷达(可探测多个方向的障碍…...

教你拥有一个自己的QQ机器人!0基础超详细保姆级教学!基于NoneBot2 Windows端搭建QQ机器人
0.序言 原文链接:教你本地化部署一个QQ机器人本教程主要面向Windows系统用户教程从0开始全程详细指导,0基础萌新请放心食用🍕如果你遇到了问题,请仔细检查是否哪一步有遗漏。如果你确定自己的操作没问题,可以到原文链…...

智能银行卡明细筛选与统计,轻松掌握账户总花销!
作为现代生活的重要组成部分,银行卡成为了我们日常消费和收入的主要途径。但是,当我们需要了解自己的银行卡账户的总花销时,繁琐的明细筛选和统计工作常常让人头疼。现在,让我们向您推荐一款智能银行卡明细筛选与统计工具…...

SRT服务器SLS
目前互联网上的视频直播有两种,一种是基于RTMP协议的直播,这种直播方式上行推流使用RTMP协议,下行播放使用RTMP,HTTPFLV或者HLS,直播延时一般大于3秒,广泛应用秀场、游戏、赛事和事件直播,满足了…...
Linux 安装 Android SDK
先安装jdk RUN apt-get install default-jdk 参考:http://t.zoukankan.com/braveym-p-6143356.html mkdir -p $HOME/install/android-sdk wget https://dl.google.com/android/repository/commandlinetools-linux-9123335_latest.zip unzip commandlinetools-linu…...

【QT开发笔记-基础篇】| 第四章 事件QEvent | 4.4 鼠标按下、移动、释放事件
本章要实现的整体效果如下: QEvent::MouseButtonPress 鼠标按下时,触发该事件,它对应的子类是 QMouseEvent QEvent::MouseMove 鼠标移动时,触发该事件,它对应的子类是 QMouseEvent QEvent::MouseButtonRel…...
vue3父子通信+ref,toRef,toRefs使用实例
ref是什么? 生成值类型的响应式数据可用于模板和reactive通过.value修改值可以获取DOM元素 <p ref”elemRef”>{{nameRef}} -- {{state.name}}</p> // 获取dom元素 onMounted(()>{ console.log(elemRef.value); }); toRef是什么? 针对一个响应式对象(rea…...

输入电压转化为电流性 5~20mA方案
输入电压转化为电流性 5~20mA方案 方案一方案二方案三 方案一 XTR111是一款精密的电压-电流转换器是最广泛应用之一。原因有二:一是线性度非常好、二是价格便宜。总结成一点,就是性价比高。 典型电路 最终电路 Z1二极管处输出电流表达式:…...

SpringBoot自带模板引擎Thymeleaf使用详解①
目录 前言 一、SpringBoot静态资源相关目录 二、变量输出 2.1 在templates目录下创建视图index.html 2.2 创建对应的Controller 2.3 在视图展示model中的值 三、操作字符串和时间 3.1 操作字符串 3.2 操作时间 前言 Thymeleaf是一款用于渲染XML/HTML5内容的模板引擎&am…...

推荐算法——Apriori算法原理
0、前言: 首先名字别读错:an pu ruo ao rui 【拼音发音】Apriori是一种推荐算法推荐系统:从海量数据中,帮助用户进行信息的过滤和选择。主要推荐方法有:基于内容的推荐、协同过滤推荐、基于关联规则的推荐、基于知识的…...
vue ant 隐藏 列
vue ant 隐藏 列 如果你使用的是Vue和Ant Design Vue组件库,你可以使用v-if指令来实现条件渲染来隐藏列。以下是一个示例代码: <template><a-table :columns"columns" :data-source"data"><template v-slot:custom…...
java基础之初始化顺序
初始化顺序 在类中变量定义的顺序决定了它们初始化的顺序。在创建任何java对象时,都是依次调用父类非静态初始化块、父类构造器执行初始化、本类的非静态初始化块、本类构造器执行初始化 public class House { // 构造器之前 Window w1 new Window(1); Ho…...

FFmpeg 命令:从入门到精通 | ffmpeg filter(过滤器 / 滤镜)
FFmpeg 命令:从入门到精通 | ffmpeg filter(过滤器 / 滤镜) FFmpeg 命令:从入门到精通 | ffmpeg filter(过滤器 / 滤镜)ffmpeg fliter 基本内置变量视频裁剪文字水印图片水印画中画视频多宫格处理 FFmpeg 命…...
【C语言】23-结构体类型
目录 1. 如何建立结构体类型2. 如何使用结构体2.1 定义结构体变量2.2 结构体变量的初始化和引用2.3 结构体数组2.4 结构体指针2.4.1 指向结构体变量的指针2.4.2 指向结构体数组的指针C 语言提供了一些由系统已定义好的数据类型,如: int、 float、 char 等,用户可以在程序…...

Python小技巧:快速合并字典dict()
文章目录 前言知识点字典合并1. dict.update()基础合并2. 字典推导式 update() 后话 前言 这里是Python小技巧的系列文章。这是第四篇,快速合并字典。 在Python的使用中,有时候需要将两个 dict(字典) 进行合并。 通常我们会借助 dict(字典) 的内置方法 …...
如何使用 React 和 Docusaurus 编写的一些自定义钩子(Hook)
import useRouteContext from @docusaurus/useRouteContext; import {DependencyList, useEffect, useRef, useState, useMemo } from react; import {dequal } from dequal; /* eslint-disable global-require */ // @ts-ignore/*** 用于深度检测依赖的useMemo钩子* @param fa…...

【初识Linux】Linux环境配置、Linux的基本指令 一
Linux基本指令一 一、学习前提(环境配置)①安装Xshell和云服务器推荐②Xshell用途如下图③打开Xshell 二、 Linux基本指令①whoami和who指令②pwd、ls、ls -l三个指令ls指令扩充 ③cd指令前提了解有了上面的认识,我们就可以开始cd指令的学习了 ④tree指令…...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...

Spark 之 入门讲解详细版(1)
1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室(Algorithms, Machines, and People Lab)开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目,8个月后成为Apache顶级项目,速度之快足见过人之处&…...
uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖
在前面的练习中,每个页面需要使用ref,onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入,需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...

令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍
文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结: 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析: 实际业务去理解体会统一注…...
Axios请求超时重发机制
Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式: 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...
爬虫基础学习day2
# 爬虫设计领域 工商:企查查、天眼查短视频:抖音、快手、西瓜 ---> 飞瓜电商:京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空:抓取所有航空公司价格 ---> 去哪儿自媒体:采集自媒体数据进…...

Redis数据倾斜问题解决
Redis 数据倾斜问题解析与解决方案 什么是 Redis 数据倾斜 Redis 数据倾斜指的是在 Redis 集群中,部分节点存储的数据量或访问量远高于其他节点,导致这些节点负载过高,影响整体性能。 数据倾斜的主要表现 部分节点内存使用率远高于其他节…...
Python ROS2【机器人中间件框架】 简介
销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...

Linux 中如何提取压缩文件 ?
Linux 是一种流行的开源操作系统,它提供了许多工具来管理、压缩和解压缩文件。压缩文件有助于节省存储空间,使数据传输更快。本指南将向您展示如何在 Linux 中提取不同类型的压缩文件。 1. Unpacking ZIP Files ZIP 文件是非常常见的,要在 …...

[ACTF2020 新生赛]Include 1(php://filter伪协议)
题目 做法 启动靶机,点进去 点进去 查看URL,有 ?fileflag.php说明存在文件包含,原理是php://filter 协议 当它与包含函数结合时,php://filter流会被当作php文件执行。 用php://filter加编码,能让PHP把文件内容…...