synchronized 锁字符串:常见坑点与解决策略

文章目录

    • 问题分析
      • 字符串常量池引发的锁复用问题
      • 字符串的不可变性
      • 使用intern() 方法以及对应的性能问题
        • 为什么不能将所有字符串都放入常量池?
          • 问题1:频繁的 Full GC
          • 问题2:性能下降
    • 使用建议
      • 不要用字符串作为锁对象
      • 使用 Google Guava 提供的 Interner 类

问题分析

在 Java 中,synchronized 关键字常被用于解决并发问题,确保线程安全。然而,当我们锁定的是字符串时,会引发一些意想不到的问题。这里分析synchronized 锁字符串相关问题点和优化方案整理。

字符串常量池引发的锁复用问题

在 JVM 中,字符串常量池是一个特殊的内存区域,用于存储字符串字面量。当两个字符串字面量相同时,它们实际上会引用常量池中的同一个对象。这意味着,如果你在不同的类或代码片段中使用了相同的字符串来作为锁对象,可能无意中锁定了同一个对象,导致跨代码段的竞争问题。

public class Test1 {private String lock1 = "lock";public void method() {synchronized (lock1) {System.out.println("Thread 1 ");}}
}public class Test2 {private String lock2 = "lock";public void method() {synchronized (lock2) {System.out.println("Thread 2 ");}}
}

问题案例:
假设系统多个流程中使用synchronized使用字符串锁,锁的信息可能是手机号,用户ID,姓名,账号等等,可能会出现相同冲突的问题,那么这种就有一定的几率出现跨代码段的竞争问题。

字符串的不可变性

字符串是不可变对象,这意味着一旦创建,字符串对象就无法被修改。当我们使用字符串作为锁时,如果在代码中不小心修改了这个字符串(例如使用 + 操作符拼接字符串),锁对象就会被改变,从而导致 synchronized 失效,线程安全性得不到保障。

使用intern() 方法以及对应的性能问题

日常中如果通过锁字符串对象的方式是锁不住字符串。因此字符串对象不是同一个地址,因此如果想要锁住字符串,需要把字符串对象添加到字符串常量池中。如果通过XXX user = XXX()的方式锁user.getUserId()是无法有效锁住的。
intern() 是 String 类中的一个方法,用于将字符串放入常量池中。具体来说,intern() 方法会检查当前字符串在常量池中是否存在。如果存在,则返回常量池中该字符串的引用;如果不存在,则将该字符串放入常量池中,并返回其引用。
使用synchronized 锁字符串,需要将字符串添加到字符串常量池中。日常使用中通过通过new对象的方式创建对象,再取对象的字段,因此需要使用intern把字符串放入常量池中,但是直接使用String的intern全部把字符串放入常量池会存在一些问题。显然在数据量很大的情况下,将所有字符串都放入常量池是不合理的,常量池大小依赖服务器内存,且只有等待fullGC,极端情况下会导致频繁fullGC。并且在数据量很大的情况下,将字符串放入常量是存在性能问题。

为什么不能将所有字符串都放入常量池?

Java 中的字符串常量池是一个专门用于存储字符串字面量的内存区域,常量池能够减少重复字符串的内存占用,提升性能。然而,常量池的大小是有限的,并且受到服务器内存的限制。将大量动态生成的字符串都放入常量池可能会带来以下几个问题:

问题1:频繁的 Full GC

常量池是 JVM 堆内存的一部分,堆内存有限。当常量池中存储了大量的字符串,且达到堆内存的上限时,JVM 可能需要进行 Full GC 来回收内存。Full GC 是一种相对耗时的操作,特别是在大数据量的场景下,它会导致应用停顿时间变长,影响系统性能。如果每次 Full GC 后,常量池内存依然不足以存储新字符串,那么 Full GC 可能会变得频繁,从而影响系统的整体稳定性和响应速度。

问题2:性能下降

intern() 方法会在常量池中查找或插入字符串对象。当字符串数据量非常大时,常量池的查找操作需要消耗一定的时间。在高并发和大数据量的场景下,这种查找操作的开销可能会变得非常明显,进而影响系统的性能表现。

使用建议

不要用字符串作为锁对象

为了避免上述问题,最佳实践是不要使用字符串作为锁对象。可以选择使用其他不可变且唯一的对象作为锁,例如 Object 或者定义一个专门的锁对象。

private final Object lock = new Object();public void method() {synchronized (lock) {System.out.println("Thread 1");}
}

使用 Google Guava 提供的 Interner 类

Guava 的 Interner 是一个接口,它的主要功能是确保相同的对象引用同一个实例。这类似于 String.intern()的功能,但更加灵活,并且不会将对象放入 JVM 的字符串常量池中,而是使用自己管理的池来存储对象。Guava 提供了两种 Interner 的实现:
• Interners.newWeakInterner():使用弱引用来存储对象,允许 GC 在内存紧张时回收这些对象。
• Interners.newStrongInterner():使用强引用来存储对象,直到这些对象不再被引用时才会被回收。

假设我们在应用中需要锁定大量动态生成的字符串,如果直接使用 String.intern() 会导致频繁的 Full GC 和性能问题,此时我们可以使用 Guava 的 Interner 类来替代。

import com.google.common.collect.Interner;
import com.google.common.collect.Interners;public class InternerExample {// 使用 Interner 来管理锁对象private final Interner<String> stringInterner = Interners.newWeakInterner();public void method(String key) {// 将字符串放入 Interner,确保相同字符串共享同一个实例String internedKey = stringInterner.intern(key);synchronized (internedKey) {// 执行同步代码块System.out.println("Locked on: " + internedKey);}}
}

使用 Interner 类替代 String.intern() 具有以下几个优点:

  1. 避免 JVM 字符串常量池的限制
    String.intern() 会将字符串放入 JVM 的字符串常量池,常量池的大小受限于堆内存,而且频繁操作可能导致 Full GC。使用 Interner,我们可以避免这些问题,因为 Interner 使用的是自己管理的对象池,池的大小和内存管理都由 Guava 来处理,不依赖于 JVM 的字符串常量池。
  2. 提供灵活的内存管理
    通过 Interners.newWeakInterner(),可以使用弱引用来管理池中的对象。这样,当 JVM 内存紧张时,GC 可以回收这些不再使用的对象,避免了堆内存过度占用的问题。

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

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

相关文章

LeetCode 3200.三角形的最大高度:枚举

【LetMeFly】3200.三角形的最大高度&#xff1a;枚举 力扣题目链接&#xff1a;https://leetcode.cn/problems/maximum-height-of-a-triangle/ 给你两个整数 red 和 blue&#xff0c;分别表示红色球和蓝色球的数量。你需要使用这些球来组成一个三角形&#xff0c;满足第 1 行…

Linux下内核空间和用户空间内存映射图详解

目录 一、简介二、内存空间定义三、内存权限四、内存空间映射图4.1 32位系统4.2 64位系统4.3 映射空间解析 五、其他相关链接1、关于linux下内存管理内容总结2、Linux内核中kzalloc分配内存时用的参数GFP_KERNEL详解3、Linux下stream内存带宽测试参数和示例详解附源码总结 一、…

HTTP cookie 与 session

一种关于登录的场景演示 - B 站登录和未登录 问题&#xff1a;B 站是如何认识我这个登录用户的&#xff1f;问题&#xff1a;HTTP 是无状态&#xff0c;无连接的&#xff0c;怎么能够记住我&#xff1f; 一、引入 HTTP Cookie 定义 HTTP Cookie&#xff08;也称为 Web Cooki…

如何区分不同类型的光源

" 声明&#xff1a;此文档中的大部分内容来源于网络&#xff0c;经校对和整理后分享给大家&#xff0c;仅供学习参考使用。" 1、问题背景 之前调试的项目中&#xff0c;客户提供的客观验收标准中要求用到TL83光源&#xff0c;用来测试图像的颜色误差及白平衡。 TL83光…

用Java爬虫API,轻松获取taobao商品SKU信息

在电子商务的世界里&#xff0c;SKU&#xff08;Stock Keeping Unit&#xff0c;库存单位&#xff09;是商品管理的基础。对于商家来说&#xff0c;SKU的详细信息对于库存管理、价格策略制定、市场分析等都有着重要作用。taobao作为中国最大的电子商务平台之一&#xff0c;提供…

windows下载配置CAS单点登录

下载 github下载 云盘瞎子啊 版本对应jdk&#xff0c;根据自身环境下载对应版本的cas。 安装 下载完成之后解压 按照.md文档执行打包命令 build.bat package配置 如果不用https&#xff0c;需要进行以下配置&#xff1a; 修改配置文件application.properties 在最后一行…

【远程监控新体验】OpenObserve结合内网穿透无公网IP远程访问全攻略

文章目录 前言1. 安装Docker2. Docker镜像源添加方法3. 创建并启动OpenObserve容器4. 本地访问测试5. 公网访问本地部署的OpenObserve5.1 内网穿透工具安装5.2 创建公网地址6. 配置固定公网地址前言 本文主要介绍如何在Linux系统使用Docker快速本地化部署OpenObserve云原生可观…

Ajax处理错误信息(处理响应报文)

<!DOCTYPE html> <html><head><meta charset"utf-8" /><title></title></head><body><form action""><div>用户名<input type"text" class"username"></div>…

时间序列神器Prophet教程2-饱和预测

公众号&#xff1a;尤而小屋编辑&#xff1a;Peter作者&#xff1a;Peter 大家好&#xff0c;我是Peter~ 本文是时间序列预测神器Prophet的第二篇&#xff1a;使用Prophet如何实现饱和预测 饱和预测增长-Saturating Forecasts 默认情况下&#xff0c;Prophet 使用线性模型来…

【C++】string类(2)

&#x1f973;个人主页: 起名字真南 &#x1f973;个人专栏:【数据结构初阶】 【C语言】 【C】 目录 引言1 模拟实现string类基本框架2 实现string类中的主要成员函数2.1 Push_Back 函数2.2 reserve 函数2.3 append 函数2.4 c_str 函数2.5 begin ,end 函数2.5 operator 函数2.6…

VScode写Java项目的教程

VScode写Java项目的教程 1.首先必选先安装Java解释器2.安装插件Java Extension Pack3.创建项目创建项目结构选择项目类型 4.测试结果源码内容 今天用一台老式笔记本写代码&#xff0c;IDEA跑不动就准备用VScode突然间就蒙了&#xff0c;怎么创建项目啊&#xff1f;于是就有了这…

自动驾驶系列—加速自动驾驶系统开发:多型号SoC快速适配的最佳实践

&#x1f31f;&#x1f31f; 欢迎来到我的技术小筑&#xff0c;一个专为技术探索者打造的交流空间。在这里&#xff0c;我们不仅分享代码的智慧&#xff0c;还探讨技术的深度与广度。无论您是资深开发者还是技术新手&#xff0c;这里都有一片属于您的天空。让我们在知识的海洋中…

Python编程探索:从基础语法到循环结构实践(下)

文章目录 前言&#x1f377;四、 字符串拼接&#xff1a;连接多个字符串&#x1f378;4.1 使用 操作符进行字符串拼接&#x1f378;4.2 使用 join() 方法进行字符串拼接&#x1f378;4.3 使用 format() 方法进行格式化拼接&#x1f378;4.4 使用 f-string&#xff08;格式化字…

OpenWRT 和 Padavan 路由器配置网络打印机 实现远程打印

本文首发于只抄博客&#xff0c;欢迎点击原文链接了解更多内容。 前言 之前有给大家介绍过 Armbian 安装 CUPS 作为打印服务器&#xff0c;像是 N1 盒子、玩客云&#xff0c;甚至是随身 WiFi 都可以通过 CUPS 来进行打印。但是有些朋友不想专门为打印机添置一个设备&#xff0…

每天5分钟玩转C#/.NET之C#语言详细介绍

C#语言介绍 C# 语言是适用于 .NET 平台&#xff08;免费的跨平台开源开发环境&#xff09;的最流行语言。 C# 程序可以在许多不同的设备上运行&#xff0c;从物联网 (IoT) 设备到云以及介于两者之间的任何设备。 可为手机、台式机、笔记本电脑和服务器编写应用。C# 是一种跨平…

iba Data Export 导出面板选项

时间线选择真实时间“Absolute date / time” 时间间隔选择0.5Sec.&#xff08;最小为0.01Sec.&#xff09; 右侧数据根据需要选择

数学建模算法与应用 第15章 预测方法

目录 15.1 微分方程模型 Matlab代码示例&#xff1a;求解简单的微分方程 15.2 灰色预测模型&#xff08;GM&#xff09; Matlab代码示例&#xff1a;灰色预测模型 15.3 自回归模型&#xff08;AR&#xff09; Matlab代码示例&#xff1a;AR模型的预测 15.4 指数平滑法 M…

1997-2022年各省农作物总播种面积数据(无缺失)

1997-2022年各省农作物总播种面积数据 1、时间&#xff1a;1997-2022年 2、来源&#xff1a;国家统计局、统计年鉴 3、指标&#xff1a;农作物总播种面积(千公顷) 4、范围&#xff1a;31省 5、缺失情况&#xff1a;无缺失 6、指标解释&#xff1a;农作物播种面积指农业生…

SCI英文文献阅读工具【全文翻译】【逐句翻译】

关注B站可以观看更多实战教学视频&#xff1a;hallo128的个人空间 SCI英文文献阅读工具【全文翻译】【逐句翻译】 1. 全文翻译【DeepL】 适用于泛读网址&#xff1a;https://www.deepl.com/zh/translator/files 1.1 前提 文档大小&#xff1a;pdf文档不超过5M&#xff08;可先…

Java实现邮件发送功能

目录 一、准备工作 二、简易文本邮件发送的实现 2.1 步骤 2.2 代码 三、复杂文件内容的发送 3.1 文件构成解析 3.2 包含图片的发送 3.3 包含附件的发送 四、实战 4.1 jsp动态页面 4.2 实体类POJO 4.3 Servlet 4.4 注册Servlet 4.5 发送邮箱核心类 一、准备工作 1、…