提高Java的锁性能

Plumbr是唯一通过解释应用程序性能数据来自动检测Java性能问题的根本原因的解决方案。

几个月前,我们在Plumbr中引入了锁定线程检测之后,我们开始收到类似于“嘿,太好了,现在我知道是什么导致了性能问题,但是现在应该做什么?”这样的查询。 打印

我们正在努力将解决方案说明构建到我们自己的产品中,但是在这篇文章中,我将分享几种独立于检测锁的工具可以应用的常见技术。 这些方法包括锁拆分,并发数据结构,保护数据(而不是代码)和减少锁范围。

锁不是邪恶的,锁争用是

每当您遇到线程代码的性能问题时,就有机会开始指责锁。 毕竟,常见的“知识”是锁很慢并且限制了可伸缩性。 因此,如果您具备了这种“知识”并开始优化代码并摆脱了锁,那么您最终有可能引入令人讨厌的并发错误,这些错误将在以后出现。

因此,了解竞争锁和非竞争锁之间的区别非常重要。 当一个线程试图进入另一个线程当前执行的同步块/方法时,发生锁争用。 现在,第二个线程被迫等待,直到第一个线程完成执行同步块并释放监视器。 当一次仅一个线程试图执行同步代码时,锁保持无竞争状态。

实际上,JVM中的同步针对无竞争的情况进行了优化,并且对于绝大多数应用程序而言,无竞争的锁几乎在执行期间没有开销。 因此,它不是性能应归咎的锁,而是争执的锁。 有了这些知识,让我们看看如何减少争用的可能性或减少争用的时间。

保护数据而不是代码

实现线程安全的一种快速方法是锁定对整个方法的访问。 例如,请看以下示例,该示例说明了构建在线扑克服务器的幼稚尝试:

class GameServer {public Map<<String, List<Player>> tables = new HashMap<String, List<Player>>();public synchronized void join(Player player, Table table) {if (player.getAccountBalance() > table.getLimit()) {List<Player> tablePlayers = tables.get(table.getId());if (tablePlayers.size() < 9) {tablePlayers.add(player);}}}public synchronized void leave(Player player, Table table) {/*body skipped for brevity*/}public synchronized void createTable() {/*body skipped for brevity*/}public synchronized void destroyTable(Table table) {/*body skipped for brevity*/}
}

作者的意图一直很好–当新玩家加入桌台时,必须保证坐在桌旁的玩家人数不会超过9人。

但是,只要这样的解决方案实际上负责使玩家坐在桌子上,即使是在流量适中的扑克场所,该系统注定要通过等待释放锁的线程不断触发争用事件。 锁定块包含帐户余额和表限制检查,这可能会涉及昂贵的操作,从而增加争用的可能性和持续时间。

解决方案的第一步是通过将同步从方法声明移到方法主体,确保我们保护数据,而不是代码。 在上面的简约示例中,它起初可能没有太大变化。 但是让我们考虑整个GameServer接口,而不仅仅是单个join()方法:

class GameServer {public Map<String, List<Player>> tables = new HashMap<String, List<Player>>();public void join(Player player, Table table) {synchronized (tables) {if (player.getAccountBalance() > table.getLimit()) {List<Player> tablePlayers = tables.get(table.getId());if (tablePlayers.size() < 9) {tablePlayers.add(player);}}}}public void leave(Player player, Table table) {/* body skipped for brevity */}public void createTable() {/* body skipped for brevity */}public void destroyTable(Table table) {/* body skipped for brevity */}
}

原本似乎是很小的更改,但现在影响了整个班级的行为。 每当玩家加入表时,先前同步的方法就会锁定在GameServer实例( this )上,并向试图同时离开 table ()表的玩家引入争用事件。 将锁从方法签名移到方法主体可推迟锁定并减少争用可能性。

缩小锁定范围

现在,在确保它是我们实际保护的数据而不是代码之后,我们应该确保我们的解决方案仅锁定必要的内容,例如,当上面的代码被重写如下时:

public class GameServer {public Map<String, List<Player>> tables = new HashMap<String, List<Player>>();public void join(Player player, Table table) {if (player.getAccountBalance() > table.getLimit()) {synchronized (tables) {List<Player> tablePlayers = tables.get(table.getId());if (tablePlayers.size() < 9) {tablePlayers.add(player);}}}}//other methods skipped for brevity
}

那么检查玩家帐户余额的潜在耗时操作(可能涉及IO操作)现在已超出锁定范围。 请注意,引入该锁仅是为了防止超出表的容量,并且帐户余额检查也不是该保护措施的一部分。

分开锁

当我们看最后一个代码示例时,您可以清楚地注意到整个数据结构都受到同一锁的保护。 考虑到我们可能在这种结构中容纳成千上万张扑克桌,由于我们必须分别保护每张桌子以防容量溢出,因此它仍然会带来争用事件的高风险。

为此,有一种简单的方法可以在每个表中引入单个锁,例如以下示例:

public class GameServer {public Map<String, List<Player>> tables = new HashMap<String, List<Player>>();public void join(Player player, Table table) {if (player.getAccountBalance() > table.getLimit()) {List<Player> tablePlayers = tables.get(table.getId());synchronized (tablePlayers) {if (tablePlayers.size() < 9) {tablePlayers.add(player);}}}}//other methods skipped for brevity
}

现在,如果我们仅同步访问同一而不是所有表的访问 ,则可以大大降低锁争用的可能性。 例如,在我们的数据结构中有100个表,争用的可能性现在比以前小100倍。

使用并发数据结构

另一个改进是删除传统的单线程数据结构,并使用为并行使用而明确设计的数据结构。 例如,当选择ConcurrentHashMap来存储所有扑克表时,将导致类似于以下代码:

public class GameServer {public Map<String, List<Player>> tables = new ConcurrentHashMap<String, List<Player>>();public synchronized void join(Player player, Table table) {/*Method body skipped for brevity*/}public synchronized void leave(Player player, Table table) {/*Method body skipped for brevity*/}public synchronized void createTable() {Table table = new Table();tables.put(table.getId(), table);}public synchronized void destroyTable(Table table) {tables.remove(table.getId());}
}

由于我们需要保护单个表的完整性,因此join()leave()方法中的同步仍然像我们之前的示例中那样。 因此, ConcurrentHashMap在这方面没有帮助。 但是,由于我们还在创建新表并在createTable()destroyTable()方法中销毁表,因此对ConcurrentHashMap的所有这些操作都是完全并发的,从而允许增加或减少并行表的数量。

其他提示和技巧

  • 降低锁的可见性。 在上面的示例中,这些锁被声明为公共锁,因此对于世界都是可见的,因此有可能其他人也会通过锁定您精心挑选的监视器来破坏您的工作。
  • 请查看java.util.concurrent.locks,以查看在那里实施的任何锁定策略是否都会改善解决方案。
  • 使用原子操作。 我们在上面的示例中实际进行的简单计数器增加实际上并不需要锁定。 用AtomicInteger替换计数跟踪中的Integer最适合此示例。

希望本文能帮助您解决锁争用问题,而无论您使用的是Plumbr 自动锁检测解决方案还是从线程转储中手动提取信息。

Plumbr是唯一通过解释应用程序性能数据来自动检测Java性能问题的根本原因的解决方案。

翻译自: https://www.javacodegeeks.com/2015/01/improving-lock-performance-in-java.html

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

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

相关文章

APK反编译工具

apktool dex2jar jd-gui 1、将要反编译的APK文件后缀改为.zip&#xff0c;解压 2、取出classes.dex文件&#xff0c;拷贝至dex2jar目录。 3、在dex2jar目录运行下列命令行&#xff1a;dex2jar.bat classes.dex&#xff0c;回车。 4、会发现该目录生成了classes.dex.dex2jar.j…

Dom属性方法

一、javascript的三大核心 1.ECMAScript js的语法标准 2.DOM Document object Model 文档对象模型&#xff0c;提供的方法可以让js操作html标签 3.BOM Browser Object Model 浏览器对象模型&#xff0c;提供的方法让js可以操作浏览器 注意&#xff1a;1. js里最大的boss是wind…

学习Netflix管理员–第2部分

为了继续上一篇有关Netflix Governator的一些基础知识的文章&#xff0c;在这里&#xff0c;我将介绍Netflix Governator带给Google Guice的另一项增强功能–生命周期管理 生命周期管理本质上提供了进入对象所经历的不同生命周期阶段的钩子&#xff0c;以引用有关Governor的Wi…

[置顶] UDP协议---心德(1)

UDP协议1.面向无连接 2.速度快 3.不可靠的协议&#xff0c;容易丢包 4.包小于64k DatagramSocket:此类表示用来发送和接收数据报包的套接字 DatagramPacket:数据包 发送端&#xff1a; 1.创建一个udpsocket服务 DatagrameSocket dsnew DatagramSocket(); 2.将数据封装到数据包…

@vue/cli 3 运行支持报错 socket

问题 /sockjs-node/info 无限报错解决方案 原因是相关代理端不支持 ws&#xff0c;因此需要在代理处关闭 ws&#xff0c;即 ws: false&#xff0c;如下&#xff1a;vue.config.js const ds_proxy {/: {ws: false,target: https://dev.test.gitinn.com/,changeOrigin: true,…

Eclipse快捷键以及设置

转自&#xff1a;http://www.blogjava.net/liudawei/articles/362468.html Eclipse实用快捷键大全 收藏 Alt左箭头,右箭头 以在编辑窗口切换标签Alt上下箭头, 以自动选择鼠标所在行,并将其上下移动Ctrlf6 可以弹出菜单…

学习Netflix管理员–第1部分

最近几天&#xff0c;我一直在与Netflix Governator合作&#xff0c;并尝试使用Governator尝试一个小样本&#xff0c;以将其与Spring Framework的依赖项注入功能集进行比较。 以下内容并不全面&#xff0c;我将在下一系列文章中对此进行扩展。 因此&#xff0c;对于没有经验的…

元素类型

元素是文档结构的基础&#xff0c;在CSS中&#xff0c;每个元素生成了一个包含了元素内容的框&#xff08;box&#xff0c;也译为“盒子”&#xff09;。但是不同的元素显示的方式会有所不同&#xff0c;例 如<div>和<span>就不同&#xff0c;而<strong>和&l…

React 等框架使用 index 做 key 的问题

React 等框架使用 index 做 key 的问题 假如有两个树&#xff0c;一个是之前&#xff0c;一个是更变之后&#xff0c;我们抽象成两种可能性。 插入内容在最后插入内容在最前 关于插在中间&#xff0c;原理一样&#xff0c;就不阐述。 使用 ul 代表树&#xff0c;并且使用了…

非捕获Lambda的实例

大约一个月前&#xff0c;我在Java 8的lambda表达式框架下总结了Brian Goetz的观点 。 目前&#xff0c;我正在研究有关默认方法的文章&#xff0c;令我惊讶的是&#xff0c;我又回到了Java处理lambda表达式的方式。 这两个功能的交集可能会产生微妙但令人惊讶的效果&#xff0…

SQL Server 查询性能优化——创建索引原则(一)

索引是什么&#xff1f;索引是提高查询性能的一个重要工具&#xff0c;索引就是把查询语句所需要的少量数据添加到索引分页中&#xff0c;这样访问数据时只要访问少数索引的分页就可以。但是索引对于提高查询性能也不是万能的&#xff0c;也不是建立越多的索引就越好。索引建少…

WordPress强制跳转https教程

在互联网火热的今天&#xff0c;安全问题显得越来越重要&#xff0c;为了用户信息安全&#xff0c;很多热门网站都启用了https 有小伙伴就问&#xff1a;我启用了https&#xff0c;为什么访问的时候显示的还是http呢&#xff1f; 其实&#xff0c;有时候并不是因为我们ssl证书…

AEM中的单元测试(大声思考)

如果要在AEM中进行单元测试&#xff0c;这不是任何建议&#xff0c;而是各种思想的总结和一些可供选择的选项。 一段时间以前&#xff0c;我已经为客户进行了一些研究&#xff0c;这篇文章在很大程度上受到了这项工作的影响&#xff0c;但是很多上下文相关的东西已经被淘汰了。…

Java 8的装饰器模式

在最近的一篇文章中&#xff0c;我描述了装饰器模式如何拯救了我的一天。 我给出了一个小代码段&#xff0c;其中包含创建装饰器的最简单方法&#xff0c;但承诺Java 8会有更好的方法。 这里是&#xff1a; 用Java 8装饰 HyperlinkListener listener this::changeHtmlViewBa…

WPF中使用流文档灵活地显示内容

WPF中使用流文档灵活地显示内容 by: Markus Egger form: http://msdn.microsoft.com/msdnmag/issues/07/08/wpf/default.aspx?loczh Windows Presentation Foundation (WPF) 提供了一系列功能。事实上&#xff0c;功能…

canvas图表(4) - 散点图

原文地址&#xff1a;canvas图表(4) - 散点图 今天开始完成散点图&#xff0c;做完这一节&#xff0c;我的canvas图表系列就算是完成了&#xff0c;毕竟平时最频繁用到的就是这几类图表了&#xff1a;柱状&#xff0c;折线&#xff0c;饼图&#xff0c;散点。经过编写canvas图表…

Java8排序–性能陷阱

Java 8带来了lambda的所有优点&#xff0c;使我们能够使用声明式样式进行编程。 但这真的免费吗&#xff1f; 我们是否应该担心必须为新的编程功能付出的代价&#xff1f; 这是一个我们可能要担心的例子。 考虑对这个简单类的实例进行排序&#xff1a; private static class…

词频统计工程相关

&#xff08;the format of this article is from SkYjoKEr&#xff09; //开始干之前 模块1、WordClass 一个存放单词以及实现相关操作的类&#xff0c;其中单词以二元组<word, freq>的形式存储。 &#xff08;20min&#xff09; 2、WordCounter 完成单词统计&#xff0…

canvas图形编辑器

原文地址&#xff1a;http://jeffzhong.space/2017/11/02/drawboard/   使用canvas进行开发项目&#xff0c;我们离不开各种线段&#xff0c;曲线&#xff0c;图形&#xff0c;但每次都必须用代码一步一步去实现&#xff0c;显得非常麻烦。有没有一种类似于PS&#xff0c;CAD…

2015年Java 8强势开始

JDK 8从2015年开始&#xff0c;其博客文章和文章的受欢迎程度将激增。 这与Java本月自动升级到JDK 8恰好吻合。 在这篇文章中&#xff0c;我列出并简要描述了2015年已经发布的有关JDK 8的众多文章和文章。 JDK 8 Streams在最近的帖子中很受欢迎。 我在2015年发表的第一篇博文是…