摘要

Java并发工具包JUC是JDK5.0引入的重要并发编程工具,提供了更高级、灵活的并发控制机制。JUC包含锁与同步器(如ReentrantLock、Semaphore等)、线程安全队列(BlockingQueue)、原子变量(AtomicInteger等)、并发集合(ConcurrentHashMap)和线程池(ThreadPoolExecutor)等核心组件。相比传统的synchronized关键字,JUC提供了更细粒度的控制、更好的性能和更丰富的功能。文章通过计数器实现、线程同步控制等实例展示了JUC组件的使用方法,并提醒开发者注意死锁、内存一致性等潜在问题。JUC大大简化了并发编程的复杂度,是Java高性能并发应用的重要基石。

什么是 JUC?

定义与背景

java.util.concurrent 是 JDK 5.0 引入的一个新包,旨在为开发者提供更高级别的并发编程支持。在此之前,Java 程序员主要依赖于 synchronized 关键字和 wait()/notify() 方法来实现线程同步和通信,这种方式虽然简单但不够灵活且容易出错。JUC 包则引入了许多新的机制和技术,如锁、信号量、线程池等,大大提升了并发程序的设计效率和可靠性。

核心理念

JUC 的设计遵循了几个重要的原则:

  • 抽象层次高:提供了比原始锁和条件变量更高层次的抽象,使得并发编程更加直观。
  • 性能优化:内部实现了多种高效算法,例如自旋锁、CAS 操作等,以减少上下文切换带来的开销。
  • 易用性强:封装了大量的复杂逻辑,让用户可以专注于业务逻辑而不必担心底层细节。
  • 可扩展性好:允许用户根据需要定制化行为,如定义自己的线程工厂或拒绝策略。

JUC 的主要组成部分

JUC 包含了多个子模块,每个模块都针对特定类型的并发问题提供了相应的解决方案。以下是其中一些关键部分:

锁与同步器

  • ReentrantLock:一个可重入的互斥锁,提供了比内置锁 (synchronized) 更丰富的功能,如尝试获取锁、定时等待锁等。
  • ReentrantReadWriteLock:读写分离的锁,允许多个读者同时访问资源,但在写操作时会阻塞所有其他线程。
  • Semaphore:信号量用于控制对共享资源的访问数量,适用于限流场景。
  • CountDownLatchCyclicBarrier:前者用于一个或多个线程等待其他线程完成某些操作;后者则是让一组线程相互等待直到满足某个条件再继续执行。

队列

  • BlockingQueue:支持阻塞插入和移除元素的队列接口,常用于生产者-消费者模式。
  • ConcurrentLinkedQueueConcurrentLinkedDeque:无锁的线程安全队列,适合高并发场景下的快速吞吐需求。
  • DelayQueue:只有当元素延迟到期后才能被取出的队列,可用于任务调度。

原子变量

  • AtomicInteger, AtomicLong, AtomicBoolean 等:提供了一组原子操作的方法,保证了在多线程环境下的安全性而无需额外加锁。
  • AtomicReference:对引用类型进行原子更新的支持。

并发集合

  • ConcurrentHashMap:高性能的哈希表实现,支持并发读写操作。
  • CopyOnWriteArrayListCopyOnWriteArraySet:基于快照机制的集合,读取时不加锁,写入时创建新副本。

线程池

  • Executors:用于创建不同类型的线程池,如固定大小、缓存式、定时任务专用等。
  • ThreadPoolExecutor:线程池的核心实现类,允许自定义线程工厂、拒绝策略等。

实战演练

接下来我们将通过几个实际的例子来展示如何使用 JUC 包中的组件解决常见的并发问题。

使用 ReentrantLock 替代 synchronized

假设我们有一个计数器类,希望它能够在多线程环境下正确地递增。传统方法可能会使用 synchronized 关键字:

public class Counter {private int count = 0;public synchronized void increment() {count++;}public synchronized int getCount() {return count;}
}

然而,如果我们想增加更多的灵活性,比如设置超时时间或者尝试非阻塞地获取锁,那么就可以考虑使用 ReentrantLock

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class Counter {private int count = 0;private final Lock lock = new ReentrantLock();public void increment() {lock.lock();try {count++;} finally {lock.unlock();}}public int getCount() {lock.lock();try {return count;} finally {lock.unlock();}}
}

这段代码中,ReentrantLock 提供了更多细粒度的操作选项,同时也保持了原有的线程安全性。

利用 CountDownLatch 实现线程同步

有时候我们需要确保某些线程必须等到其他线程完成了特定的工作才能继续执行。例如,在启动多个异步任务之前,主线程可能需要等待所有准备工作都已完成。这时可以使用 CountDownLatch 来实现:

import java.util.concurrent.CountDownLatch;public class Example {public static void main(String[] args) throws InterruptedException {int numThreads = 3;CountDownLatch startSignal = new CountDownLatch(1);CountDownLatch doneSignal = new CountDownLatch(numThreads);for (int i = 0; i < numThreads; ++i) {new Thread(new Worker(startSignal, doneSignal)).start();}// 让工作线程准备好System.out.println("Main thread is ready.");startSignal.countDown();  // 向工作线程发出开始信号// 等待所有工作线程完成doneSignal.await();System.out.println("All threads have finished.");}static class Worker implements Runnable {private final CountDownLatch startSignal;private final CountDownLatch doneSignal;Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {this.startSignal = startSignal;this.doneSignal = doneSignal;}@Overridepublic void run() {try {startSignal.await();  // 等待开始信号doWork();doneSignal.countDown();  // 完成后通知主调方} catch (InterruptedException e) {Thread.currentThread().interrupt();}}private void doWork() {// 模拟工作System.out.println(Thread.currentThread().getName() + " is working...");try {Thread.sleep(1000);  // 模拟耗时操作} catch (InterruptedException e) {Thread.currentThread().interrupt();}}}
}

在这个例子中,CountDownLatch 被用来协调主线程和工作线程之间的同步关系,确保所有准备工作完成后才真正启动任务。

使用 ConcurrentHashMap 替换 HashMap

当我们需要在一个多线程环境中频繁读写 Map 结构的数据时,ConcurrentHashMap 可以提供更好的性能和线程安全性:

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;public class Example {private static final Map<String, String> map = new ConcurrentHashMap<>();public static void main(String[] args) {// 添加数据map.put("key1", "value1");map.putIfAbsent("key2", "value2");// 读取数据String value = map.get("key1");// 更新数据map.computeIfPresent("key1", (k, v) -> "newValue");// 删除数据map.remove("key2");// 遍历数据map.forEach((k, v) -> System.out.println(k + "=" + v));}
}

ConcurrentHashMap 内部采用了分段锁技术,只对涉及修改的部分进行锁定,从而提高了并发访问的效率。

注意事项

尽管 JUC 包提供了很多便利的功能,但在实际应用中也需要注意一些潜在的问题:

  • 死锁风险:不当使用锁可能导致死锁现象,因此应该尽量避免嵌套锁,并遵守一致的加锁顺序。
  • 内存一致性错误:即使使用了线程安全的数据结构,也不能忽视可见性和有序性的保证。必要时可以借助 volatile 关键字或原子变量。
  • 性能瓶颈:过多的同步操作可能成为系统的性能瓶颈,所以要权衡好线程安全性和执行效率之间的关系。
  • 异常处理:任何时候都不要忽略对异常情况的处理,尤其是在并发环境中,未捕获的异常可能会导致不可预测的行为。

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

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

相关文章

零基础学后端-PHP语言(第二期-PHP基础语法)(通过php内置服务器运行php文件)

经过上期的配置&#xff0c;我们已经有了php的开发环境&#xff0c;编辑器我们继续使用VScode&#xff0c;如果是新来的朋友可以看这期文章来配置VScode 零基础学前端-传统前端开发&#xff08;第一期-开发软件介绍与本系列目标&#xff09;&#xff08;VScode安装教程&#x…

扩散模型逆向过程详解:如何从噪声中恢复数据?

在扩散模型中&#xff0c;逆向过程的目标是从噪声数据逐步恢复出原始数据。本文将详细解析逆向条件分布 q(zt−1∣zt,x)q(\mathbf{z}_{t-1} \mid \mathbf{z}_t, \mathbf{x})q(zt−1​∣zt​,x)的推导过程&#xff0c;揭示扩散模型如何通过高斯分布实现数据重建。1. 核心问题 在…

2025年7月份实时最新获取地图边界数据方法,省市区县街道多级联动【文末附实时geoJson数据下载】

动态生成最新行政区划 GeoJSON 数据并结合 ECharts 实现地图下钻功能 在开发基于地图的数据可视化应用时&#xff0c;一个常见的挑战是获取准确且最新的行政区划边界数据&#xff08;GeoJSON&#xff09;。许多现有的在线资源可能数据陈旧&#xff0c;无法反映最新的行政区划调…

Spark实现WorldCount执行流程图

spark可以分区并行执行&#xff0c;同时并行执行也可以基于内存完成迭代代码对于大部分spark程序来说都是以driver开始driver结束&#xff0c;中间都是executor分布式运行

编程与数学 03-002 计算机网络 02_网络体系结构与协议

编程与数学 03-002 计算机网络 02_网络体系结构与协议一、网络体系结构的基本概念&#xff08;一&#xff09;分层体系结构的优点&#xff08;二&#xff09;协议、接口与服务的概念二、OSI参考模型&#xff08;一&#xff09;七层模型的层次划分及功能&#xff08;二&#xff…

Flutter 提取图像主色调 ColorScheme.fromImageProvider

从图像中提取主色调&#xff0c;用于动态适配颜色主题或者界面颜色。之前在 Flutter 应用里一直用的 palette_generator 插件&#xff0c;可以分析图像颜色&#xff0c;从中提取一系列主要的色调。最近发现这个谷歌官方的插件竟然不维护了&#xff0c;后续没有更新计划了。 查找…

51c自动驾驶~合集8

自己的原文哦~ https://blog.51cto.com/whaosoft/11618683 #Hierarchical BEV BEV进入定制化时代&#xff01;清华Hierarchical BEV&#xff1a;创新多模块学习框架&#xff0c;无痛落地无缝量产&#xff01;​ 论文思路 自动驾驶指通过传感器计算设备、信息通信、自…

Excel——重复值处理

识别重复行的三种方法方法1&#xff1a;COUNTIF公式法在E2单元格输入公式&#xff1a;COUNTIF($B$2:$B2,B2)>1下拉填充至所有数据行结果为TRUE的即为重复行&#xff08;会标出第二次及以后出现的重复项&#xff09;方法2&#xff1a;排序IF公式法按商机号排序&#xff08;数…

华普微Matter模块HM-MT7201,打破智能家居生态孤岛

随着智能家居渗透率与认可度的持续提升&#xff0c;消费者对于智能家居的功能诉求正从具备联网控制、远程控制与语音遥控等基础交互能力&#xff0c;升级为能通过单一的家居生态平台APP无缝控制所有的品牌设备&#xff0c;从而实现真正意义上的统一调度。这种从“单一设备联网控…

如何使用 minio 完成OceanBase社区版的归档和备份

自OceanBase社区版4.2.1BP7版本起&#xff0c;OceanBase的归档与备份功能开始兼容AWS S3及S3协议的对象存储服务&#xff0c;因此&#xff0c;许多用户选择采用 MinIO 作为其备份存储介质。因为 MinIO 兼容AWS S3云存储服务接口&#xff0c;成为了一个轻便的服务选项。 本文将…

Nacos-服务注册,服务发现(二)

Nacos健康检查 两种健康检查机制 Nacos作为注册中⼼, 需要感知服务的健康状态, 才能为服务调⽤⽅提供良好的服务。 Nacos 中提供了两种健康检查机制&#xff1a; 客⼾端主动上报机制&#xff1a; 客⼾端通过⼼跳上报⽅式告知服务端(nacos注册中⼼)健康状态, 默认⼼跳间隔5…

手写PPO_clip(FrozenLake环境)

参考&#xff1a;白话PPO训练 成功截图 算法组件 四大部分 同A2C相比&#xff0c;PPO算法额外引入了一个old_actor_model. 在PPO的训练中&#xff0c;首先使用old_actor_model与环境进行交互得到经验&#xff0c;然后利用一批经验优化actor_model&#xff0c;最后再将actor_m…

人形机器人指南(八)操作

八、环境交互与操作能力——人形机器人的“灵巧双手”环境交互与操作能力是人形机器人区别于移动平台的核心能力标志。通过仿生学设计的运动链与智能控制算法&#xff0c;机器人得以在非结构化环境中执行抓取、操纵、装配等复杂任务。本章将系统解析机械臂运动学架构、灵巧手设…

管理 GitHub Pages 站点的自定义域(Windows)

管理 GitHub Pages 站点的自定义域(Windows) 你可以设置或更新某些 DNS 记录和存储库设置,以将 GitHub Pages 站点的默认域指向自定义域。 谁可以使用此功能? GitHub Pages 在公共存储库中提供 GitHub Free 和 GitHub Free for organizations,在公共和私有存储库中提供 Gi…

【PCIe 总线及设备入门学习专栏 5.1.3 -- PCIe PERST# 时序要求】

文章目录 Overview 什么是PERST# 第一条要求 术语解释 要求含义 第二条要求 术语解释 要求含义 Perst 示例说明 过程如下 总结 Overview 首先我们看下 PCIe x协议对 PERST 的要求: A component must enter the LTSSM Detect state within 20 rms of the end of Fundamental R…

图像认知与OpenCV——图像预处理

目录 一、颜色加法 颜色加法 颜色加权加法 示例 二、颜色空间转换 RGB转Gray&#xff08;灰度&#xff09; RGB转HSV HSV转RGB 示例 三、灰度化 最大值法 平均值法 加权平均值法 四、图像二值化处理 阈值法 反阈值法 截断阈值法 低阈值零处理 超阈值法 OTSU…

Vue 3 组件通信全解析:从 Props 到 Pinia 的深入实践

引言 Vue 3 作为现代前端框架的代表之一&#xff0c;以其灵活性和高效性受到开发者的广泛喜爱。在 Vue 3 中&#xff0c;组件是构建用户界面的核心单元&#xff0c;而组件之间的通信则是实现动态交互和数据流动的关键环节。无论是简单的父子组件通信&#xff0c;还是复杂的跨组…

CodeBuddy IDE实战:用AI全栈能力快速搭建课程表网页

声明&#xff1a;本文仅是实践测评&#xff0c;并非广告 1.前言 在数字化开发的浪潮中&#xff0c;工具的革新往往是效率跃迁的起点。腾讯云 CodeBuddy IDE 是 “全球首个产设研一体 AI 全栈开发平台” &#xff0c;它不仅打破了产品、设计与研发的职能壁垒&#xff0c;更重新…

11. HTML 中 DOCTYPE 的作用

总结H5 的声明HTML5 的 DOCTYPE 声明 HTML5 中的 <!DOCTYPE html> 声明用于告诉浏览器当前文档使用的是 HTML5 的文档类型。它必须是文档中的第一行内容&#xff08;在任何 HTML 标签之前&#xff09;&#xff0c;以确保浏览器能够正确地解析和渲染页面。DOCTYPE 的作用 …

Linux C 网络基础编程

基础知识在进行网络编程之前&#xff0c;我们需要简单回顾一下计算机网络五层模型的网络层和传输层&#xff0c;这两层在面向后端编程时用的最多。物理层和链路层过于底层&#xff0c;已经完全由内核协议栈实现&#xff0c;不再细述。这里假设读者已经对计算机网络有一个大致的…