深入理解Java中String.intern()方法:从原理到并发控制实践

深入理解 Java 中 String.intern () 方法:从原理到并发控制实践

在 Java 开发中,String.intern()方法是一个看似简单却蕴含深意的 API。它在字符串常量池管理、内存优化以及并发控制等场景中有着关键作用。本文将从底层原理出发,结合实际案例详细解析intern()方法的作用,并重点探讨其在并发控制中确保锁对象唯一性的经典应用。

一、Java 字符串的存储机制:常量池与堆的分工

要理解intern()方法,首先需要掌握 Java 中字符串的存储特性。Java 中的字符串有两种主要存储方式:

  1. 字符串常量池(String Constant Pool)

    这是 JVM 为了优化字符串存储而设计的特殊内存区域,用于存储字面量字符串(如"abc")和通过intern()方法加入的字符串。常量池的核心作用是复用字符串对象—— 相同内容的字符串在常量池中只会保留一份,避免重复创建相同对象造成的内存浪费。

  2. 堆内存(Heap)

    当通过new String("xxx")方式创建字符串时,JVM 会在堆中创建一个新的字符串对象,同时如果常量池中没有对应的字面量,会将字面量存入常量池。此时,堆中的对象持有指向常量池字面量的引用。

二、String.intern () 方法的核心作用:确保引用唯一性

intern()是 String 类的一个 native 方法,其官方定义为:

当调用intern()方法时,如果常量池中已经存在一个与当前字符串内容相同的字符串,则返回常量池中该字符串的引用;如果不存在,则将当前字符串加入常量池,并返回当前字符串的引用。

简单来说,intern()的核心作用是将字符串 “入池” 并返回常量池中唯一的引用,确保 “内容相同的字符串” 在内存中指向同一个对象。

实例验证:intern () 如何统一引用

我们通过代码验证intern()的效果:

public class InternDemo {public static void main(String\[] args) {// 情况1:直接定义字面量,默认入池String s1 = "abc";String s2 = "abc";System.out.println(s1 == s2); // true(引用相同,都指向常量池对象)// 情况2:new创建的字符串对象在堆中       String s3 = new String("abc");String s4 = new String("abc");System.out.println(s3 == s4); // false(堆中两个不同对象)// 情况3:调用intern()后,引用指向常量池String s5 = s3.intern();String s6 = s4.intern();System.out.println(s5 == s6); // true(均指向常量池的"abc")System.out.println(s5 == s1); // true(与字面量引用一致)}}

输出结果清晰地展示了intern()的作用:无论堆中创建了多少个内容相同的字符串对象,通过intern()处理后,它们的引用都会指向常量池中唯一的对象。

三、为什么在并发控制中需要 intern ()?

在多线程场景中,synchronized关键字是控制并发的常用手段,但其生效的核心前提是锁对象的引用必须唯一—— 如果两个线程使用不同的锁对象(即使内容相同),synchronized将无法保证同步效果,可能导致数据不一致。

以用户点赞功能为例,假设我们需要通过用户 ID 作为锁对象,确保同一用户的点赞操作串行执行(避免并发导致点赞数错乱)。如果直接使用userId.toString()作为锁对象,会存在隐藏风险:

问题场景:未使用 intern () 的锁失效风险

// 模拟多线程环境下的点赞操作public class ThumbUpService {// 模拟点赞数存储private Map\<Long, Integer> thumbCountMap = new ConcurrentHashMap<>();public void doThumbUp(Long userId) {// 直接使用userId.toString()作为锁对象synchronized (userId.toString()) {int currentCount = thumbCountMap.getOrDefault(userId, 0);thumbCountMap.put(userId, currentCount + 1);}}}

上述代码存在隐患:userId.toString()每次调用都会创建一个新的 String 对象(堆中),即使userId的值相同,两次调用toString()生成的字符串引用也不同。例如:

Long userId = 10086L;
String lock1 = userId.toString();
String lock2 = userId.toString();
System.out.println(lock1 == lock2); // false(引用不同)

这意味着,同一用户的两次点赞操作可能会获得不同的锁对象,导致synchronized失效,最终出现点赞数统计错误(如并发写入导致的计数丢失)。

解决方案:用 intern () 确保锁对象唯一

要解决上述问题,只需对userId.toString()调用intern()方法:

public void doThumbUp(Long userId) {// 使用intern()确保锁对象唯一synchronized (userId.toString().intern()) {int currentCount = thumbCountMap.getOrDefault(userId, 0);thumbCountMap.put(userId, currentCount + 1);}}

此时,无论userId.toString()创建多少个堆对象,intern()都会返回常量池中唯一的字符串引用。例如:

Long userId = 10086L;
String lock1 = userId.toString().intern();
String lock2 = userId.toString().intern();
System.out.println(lock1 == lock2); // true(引用相同)

这样一来,同一用户的所有并发操作都会竞争同一个锁对象,synchronized能正确保证操作的串行执行,避免数据不一致。

四、源码解析:intern () 在实际项目中的典型应用

在开篇提到的 “取消点赞” 方法中,intern()的作用正是确保锁对象唯一:

public Boolean undoThumb(DoThumbRequest doThumbRequest, HttpServletRequest request) {User loginUser = userService.getLoginUser(request);// 关键:用intern()确保同一用户的锁对象唯一synchronized (loginUser.getId().toString().intern()) {// 业务逻辑:取消点赞(更新点赞数、删除点赞记录)// ...}}

这里的核心逻辑是:

  • loginUser.getId()返回用户唯一 ID(如 10086);

  • toString()将 ID 转为字符串(如 “10086”);

  • intern()确保该字符串在常量池中只有唯一引用。

无论同一用户触发多少次 “取消点赞” 操作,synchronized始终会获取同一个锁对象,从而保证并发安全。

五、intern () 的其他重要特性与注意事项

  1. 性能与内存考量

    intern()的实现依赖字符串常量池(JDK 7 + 后常量池移至堆中),其底层通常通过哈希表实现,因此intern()的调用本质是一次哈希表的查询 / 插入操作,时间复杂度为 O (1)(理论上)。但频繁对大量不同字符串调用intern()可能导致常量池膨胀,需在 “引用唯一性” 和 “内存占用” 间权衡。

  2. 与字符串常量的区别

    字面量字符串(如"abc")默认会入池,而new String("abc")创建的对象需显式调用intern()才会入池。例如:

String s1 = "abc"; // 直接入池
String s2 = new String("abc").intern(); // 显式入池,与s1引用相同
  1. JDK 版本兼容性

    虽然intern()的核心逻辑在各 JDK 版本中保持一致,但常量池的存储位置(JDK 6 及之前在方法区,JDK 7 + 在堆中)和实现细节可能略有差异,不过这对intern()的使用方式和最终效果无影响。

  2. 避免过度使用

    并非所有字符串都需要intern(),仅在需要 “强制复用相同内容的字符串引用” 时使用(如锁对象、缓存键等场景)。

六、总结

String.intern()方法看似简单,却深刻体现了 Java 对字符串优化的设计思想。其核心价值在于确保内容相同的字符串在内存中拥有唯一引用,这一特性使其在并发控制中成为保证锁对象唯一性的关键手段。

在实际开发中,理解intern()的原理不仅能帮助我们写出更安全的并发代码,还能在字符串优化、内存管理等场景中做出更合理的设计决策。希望本文能让你对intern()方法有更深入的理解,在面对类似问题时能灵活运用这一强大工具。

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

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

相关文章

在Linux中创建LVGL应用

在Linux中创建LVGL应用 简介 上一篇文章介绍了在imx6上开发UI的流程 . 这篇接上文&#xff0c; 介绍具体的开发步骤。 1. 创建项目主目录 mkdir my_lvgl_project cd my_lvgl_project2. 初始化 Git 仓库 (可选但推荐) git init echo "# My Project with Dependencies&…

大模型对比评测:Qwen2.5 VS Gemini 2.0谁更能打?

一、背景与选型关键 在 AI 应用落地的时代&#xff0c;“AI大模型选型对比”成为关键环节。选择合适的模型要综合考量性能、上下文长度、推理能力、中文/编程支持、成本等多维度指标。 本文重点比较 Gemini2.0Flash-Lite &#xff08;Preview&#xff09;、Gemini2.0Flash &a…

转置卷积解释与示例计算

文章目录转置卷积的三种等价实现方法&#xff1a;原理、公式与等价性分析数学定义与核心公式方法一&#xff1a;零填充翻转核卷积&#xff08;数学定义方法&#xff09;原理与公式等价性说明方法二&#xff1a;直接位置映射&#xff08;pytorch框架高效实现&#xff09;原理与公…

关于车位引导及汽车乘梯解决方案的专业性、系统性、可落地性强的综合设计方案与技术实现说明,旨在为现代智慧停车楼提供高效、安全、智能的停车体验。

一、系统概述随着城市土地资源日益紧张&#xff0c;立体停车、自动化停车成为发展趋势。本方案围绕“车位引导系统 汽车乘梯系统”构建智慧停车核心体系&#xff0c;结合地磁/视频/超声波检测、AI识别、语音交互、电梯自动调度等先进技术&#xff0c;实现车辆入场、引导、停泊…

【相机】曝光时间长-->拖影

曝光时间长 → 运动目标在快门开启期间持续移动 → 同一像素记录多个位置的能量 → 图像出现“拖影”&#xff08;运动模糊&#xff09;。&#x1f50d; 具体原因卷帘快门&#xff08;Rolling Shutter&#xff09;效应 RealSense 的 RGB 传感器&#xff08;如 IMX 系列&#xf…

day36 力扣1049.最后一块石头的重量II 力扣494.目标和 力扣474.一和零

最后一块石头的重量II有一堆石头&#xff0c;用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量。每一回合&#xff0c;从中选出任意两块石头&#xff0c;然后将它们一起粉碎。假设石头的重量分别为 x 和 y&#xff0c;且 x < y。那么粉碎的可能结果如下&#…

Java内存模型(Java Memory Model,JMM)

​​ JMM​​ 是Java虚拟机&#xff08;JVM&#xff09;规范中定义的一组规则和规范&#xff0c;用于描述多线程环境下&#xff0c;Java程序中变量的访问和修改行为&#xff0c;尤其是在并发编程中如何保证内存可见性、原子性和有序性。JMM 是 Java 并发编程的基石&…

【swoole Windows 开发(swoole-cli 开发 hyperf)】

先前swoole在Windows平台的开发体验极差&#xff0c;如果在Windows开发swoole的东西可以用docker或者虚拟机&#xff0c;远程开发&#xff0c;体验比较好的是直接Mac或者Linux系统开发。但是作为window平台的钉子户表示我穷。swoole之前已经推出了cygwin64编译成winwods版本的方…

兴达餐饮 酒店 进销存管理系统软件

兴达餐饮 酒店 进销存管理系统软件

Seal Report:一款免费开源的报表工具

Seal Report 是一款基于 C# 语言开发的开源报表工具&#xff0c;可以从各种数据库或 NoSQL 数据源中生成日常报告&#xff0c;并且执行复杂的计划任务。 功能特性 免费开源&#xff1a;源代码托管在 GitHub 上&#xff0c;用户可以自由使用、修改、甚至集成到自己的系统中&…

WebRTC 多媒体 SDP 示例与解析

webRTC中的SDP的Bundlle可能包含一个或者多个媒体块&#xff08;媒体描述, 源码对应类ContentInfo&#xff09;&#xff0c;从 m 开始到下一个 m 行&#xff08;或 SDP 结束&#xff09;之间的所有属性&#xff08;包括 a&#xff09;都属于同一个媒体块&#xff08;media sect…

SpringBoot 启动富文本文字更改

正常来说 SpringBoot启动时候&#xff0c;展示的文字是这个 、 主播这边想要换一个样式&#xff0c;换一个自己自定义的文字 这边换成了自己的博客名字 具体实现操作如下 在项目目录 resources下创建一个名字为banner.txt的文本&#xff0c;这是SpringBoot启动的时候寻找的…

基于结构熵权-云模型的铸铁浴缸生产工艺安全评价

一、评价模型核心思想 结构熵权法 解决传统熵权法忽略指标间结构关系的问题,通过指标层次网络计算权重。 步骤: 构建工艺安全评价指标体系(树状/网络结构) 计算同级指标间的影响度矩阵 引入修正熵权:wj=1−Ej∑(1−Ek)结构影响因子w_j = \frac{1 - E_j}{\sum (1 - E_k)} \…

[Linux]从零开始的vs code交叉调试arm Linux程序教程

一、前言 最近的项目中需要集成rknn的视觉识别&#xff0c;在这之前我并且没有将rknn集成到自己项目的经验。这里我需要在rknn原本demo的基础上我还需要集成自己的业务代码。但是又有一个问题&#xff0c;原本rknn我们都是使用交叉编译编译到开发板上的&#xff0c;并且我们还要…

视频号私信自动化回复插件

给自己的浏览器插件又增加了视频号斯信的自动化回复搜索&#xff1a;程序员老狼主体逻辑就是&#xff0c;不停的点击打招呼和斯信那个tab切换查看有无小红点&#xff0c;有小红点的会话&#xff0c;就点击。查看有无打招呼&#xff0c;有打招呼就点击&#xff0c;抓取昵称和内容…

Web前端实现银河粒子流动特效的3种技术方案对比与实践

文章目录 前端实现银河粒子流动特效的技术原理与实践 引言:银河粒子特效的技术背景与现状 技术发展历史 当前技术现状 技术原理与实现方案 思维导图:银河粒子特效技术架构 1. CSS3实现方案 基础实现代码 性能优化技巧 2. Canvas 2D实现方案 基础实现代码 Canvas高级优化技术 …

Linux:告别Jammy,拥抱Noble!WSL Ubuntu 22.04 到 24.04 LTS 终极升级指南

大家好&#xff01;如果大家和我一样&#xff0c;是Windows Subsystem for Linux (WSL) 的忠实用户&#xff0c;那么大家一定对Ubuntu在其中的表现印象深刻。我们中的许多人可能还在使用稳定可靠的Ubuntu 22.04 LTS (Jammy Jellyfish)。但现在&#xff0c;一个更令人兴奋的时代…

江协科技STM32 11-1 SPI通信协议

本节课我们将继续学习下一个通信协议&#xff0c;SPI。SPI通信和我们刚刚学习过的I2C通信差不多。两个协议的设计目的都一样都是实现主控芯片和各种外挂芯片之间的数据交流&#xff0c;有了数据交流的能力&#xff0c;我们的主控芯片就可以挂载并操纵各式各样的外部芯片&#x…

预过滤环境光贴图制作教程:第一步 - HDR 转立方体贴图

在基于物理的渲染(PBR)中,环境光贴图是实现真实光照效果的核心组件之一。而将 HDR 全景图转换为立方体贴图,是制作预过滤环境光贴图的基础步骤。本教程将详细讲解如何实现这一转换过程。 什么是 HDR 转立方体贴图? HDR(高动态范围)全景图通常以等矩形投影(Equirectan…

02 深度学习介绍【动手学深度学习v2】| 学习笔记

1、intro自然语言处理虽然我们过去取得了很大的进展&#xff0c;但是实际上还是停留在感知层面。计算机视觉领域&#xff0c;因为图片里面都是像素&#xff0c;像素很难用符号学来解释&#xff0c;所以计算机视觉大部分是用概率模型或机器学习来做。深度学习它是机器学习的一种…