提高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,一经查实,立即删除!

相关文章

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

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

非捕获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证书…

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图表…

词频统计工程相关

&#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…

Sacrilege –自定义SWT滚动条

SWT是本机OS小部件之上的薄抽象层。 如果您打算将应用程序与OS外观很好地集成在一起&#xff0c;那将是一件非常好的事情。 但是&#xff0c;作为一种折衷方案&#xff0c;这种方法大大限制了样式功能。 特别是&#xff0c;我感觉到本机SW​​T滚动条通常会干扰更精细的视图布…

CSS3盒模型温故

CSS有一种基础设计模式叫盒模型&#xff0c;定义了Web页面中的元素是如何看做盒子来解析的。每一个盒子有不同的展示界面&#xff0c;下面就来介绍盒模型&#xff0c;主要有一下几种盒模型&#xff1a;inline、inline-block、block、table、absolute position、float。浏览器把…

SSL与WildFly 8和Undertow

我一直在研究WildFly 8的一些安全性主题&#xff0c;偶然发现了一些配置文档&#xff0c;这些文档没有很好地记录。 其中之一是新Web子系统Undertow的TLS / SSL配置。 有许多关于较旧的Web子系统的文档&#xff0c;并且确实仍然可以使用&#xff0c;但是这里是使用新方法进行配…

CSS属性选择器温故-4

1.属性选择器就是通过元素属性来找到元素 2.属性选择器语法 CSS3遵循了惯用的编码规则&#xff0c;通配符的使用提高了样式表的书写效率&#xff0c;也使CSS3的属性选择器更符合编码习惯 3.浏览器兼容性 CSS选择器总结&#xff1a;CSS选择器和jQuery的选择器非常相似&#xff…

Python多篇新闻自动采集

昨天用python写了一个天气预报采集&#xff0c;今天趁着兴头写个新闻采集的。 目标是&#xff0c;将腾讯新闻主页上所有新闻爬取下来&#xff0c;获得每一篇新闻的名称、时间、来源以及正文。 接下来分解目标&#xff0c;一步一步地做。 步骤1&#xff1a;将主页上所有链接爬取…

使用navicat premium将数据库从Oracle迁移到SQL Server,或从Oracle迁移到MySQL

有时候我们有迁移数据库的需求&#xff0c;例如从Oracle迁移到SQL Server&#xff0c;或者从MySQL迁移到Oracle。 很多江湖好汉一时不知如何手工操作&#xff0c;所幸的是Navicat提供了迁移的自动化操作界面。 当然&#xff0c;Navicat的数据库迁移无法做到完美&#xff0c;一些…

书评:Mockito Essentials

Sujoy Acharya的Mockito Essentials副标题&#xff08; Packt Publishing &#xff0c;2014年10月&#xff09;是&#xff1a;“实用指南&#xff0c;可帮助您使用Mockito进行单元测试并开始运行。” Mockito Essentials中的前言和七章涵盖大约190个实质性页面。 前言 在序言中…

http网站转换成https网站

https&#xff0c;https的本地测试环境搭建,asp.net结合https的代码实现,http网站转换成https网站之后遇到的问题 一&#xff1a;什么是https SSL&#xff08;Security Socket Layer&#xff09;全称是加密套接字协议层&#xff0c;它位于HTTP协议层和TCP协议层之间&#x…

EAGER的获取是代码的味道

介绍 休眠获取策略确实可以使几乎没有爬网的应用程序和响应速度很快的应用程序有所不同。 在这篇文章中&#xff0c;我将解释为什么您应该选择基于查询的获取而不是全局获取计划。 取得101 Hibernate定义了四种关联检索策略 &#xff1a; 提取策略 描述 加入 原始SELECT语…

基于S2SH的电子商务网站系统性能优化

对于经常逛网页的人来说最受不了的事情就是访问的网页加载太慢&#xff0c;除去网络的原因网站的系统性能对加载的快慢非常重要&#xff0c; 网上有个统计&#xff1a; 每慢500ms Google访问量降低20% 每慢400ms Yahoo 访问量降低5-9% 每慢100ms Amazon销售额降低1% 对于商务网…

ExtJs的Reader

ExtJs的Reader Reader : 主要用于将proxy数据代理读取的数据按照不同的规则进行解析,讲解析好的数据保存到Modle中 结构图 Ext.data.reader.Reader 读取器的根类 Ext.data.reader.Json JSON格式的读取器 Ext.data.reader.Array 扩展JSON的Array读取器 Ext.data.reader.Xml XML格…