java锁性能对比_提高Java的锁性能

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

java锁性能对比

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

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

相关文章

binlog日志_MySQL三大日志binlog、redo log和undo log

点击蓝色“JavaKeeper”关注我哟加个“星标”&#xff0c;一起成长&#xff0c;做牛逼闪闪的技术人Keeper导读&#xff1a;日志是mysql数据库的重要组成部分&#xff0c;记录着数据库运行期间各种状态信息。mysql日志主要包括错误日志、查询日志、慢查询日志、事务日志、二进制…

C 语言精髓之变参函数

我们以 printf 这个 very 熟悉的函数为例&#xff0c;来分析一下变参函数。先看下 printf 函数的定义&#xff1a;int printf(const char *fmt, ...){ int i; int len; /* va_list 即 char * */va_list args;va_start(args, fmt); /* 内部使用了 va_arg() */len v…

lambda捕获this_非捕获Lambda的实例

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

mysql8 安装_MySQL8.x安装使用

1.下载网址https://dev.mysql.com/downloads/mysql/下载要登录&#xff0c;可以使用Oracle账户登录2.安装MySQL服务下载好的解压到D:developer目录下配置MySQL(windows下是my.ini&#xff0c;Linux下是my.cnf)[mysql]# 设置mysql客户端默认字符集default-character-setutf8[mys…

从原理到方法,一文讲清如何应对C语言内存泄露!

可能不少开发者都遇到过内存泄漏导致的网上问题&#xff0c;具体表现为单板在现网运行数月以后&#xff0c;因为内存耗尽而导致单板复位现象。一方面&#xff0c;内存泄漏问题属于比较浅显的错误&#xff0c;此类问题遗漏到现网&#xff0c;影响不好&#xff1b;另一方面&#…

openshift_OpenShift上具有NetBeans的Java EE

openshift今天是慕尼黑的NetBeans日 。 我很高兴提出一个关于将Red Hat产品与我著名的IDE集成的会议。 因此&#xff0c;我一直在谈论WildFly &#xff0c; EAP &#xff0c;Git和OpenShift Online&#xff0c;并展示了使用该工具集优化开发工作流程的所有不同方式。 大约有10…

const char * 类型的实参与 char * 类型的形参不兼容_4 种 C++ 强制类型转换,你都清楚吗?...

我们先来回忆以下&#xff0c;C 语言的强制类型转换形式&#xff1a;(type) expr;这种旧式强制类型转换从表现形式上来说不够清晰明了&#xff0c;容易看漏&#xff0c;一旦转换过程出现问题&#xff0c;追踪起来也就更加困难。为了解决以上问题&#xff0c;C不仅兼容了C的强制…

ggplot2中显示坐标轴_R可视化08|ggplot2图层标度图层(scale layer)图例篇

"pythonic生物人"的第106篇分享本文详细介绍ggplot2中图例标度(legends scales)&#xff0c;续前篇R可视化07|ggplot2图层-标度图层(scale layer)-颜色盘篇本文目录4、图例标度(legends scale)图例位置设置修改ggplot2的图例符号ggplot2的图例顺序|方向等花里胡哨设置…

C explicit 关键字详解

explicit关键字的作用explicit关键字在写程序时使用的次数较少,但是仔细观察会发现,在C 标准库中的相关类声明中explicit出现的频率是很高的,那么explicit关键字到底有什么作用呢?接下来我就为大家一一解答。explicit为清晰的;明确的之意.顾名思义,关键字explicit可以阻止隐式…

python决策树算法_决策树算法及python实现

决策树算法是机器学习中的经典算法 1.决策树(decision tree) 决策树是一种树形结构&#xff0c;其中每个内部节点表示一个属性上的测试&#xff0c;每个分支代表一个测试输出&#xff0c;每个叶节点代表一种类别。 假设小明去看电影&#xff0c;影响看电影的外部因素有 时间 电…

长见识:你真的知道C语言里extern quot;Cquot; 的作用吗?

经常在C语言的头文件中看到下面的代码&#xff1a;#ifdef __cplusplus extern "C" { #endif// all of your legacy C code here#ifdef __cplusplus } #endif这通常用于C 和C混合编程的时候&#xff0c;为了防止C 的编译器在编译C文件的时候出现错误&#xff1b;众所周…

python自动批量发邮件脚本_Python实现自动发送邮件功能

简单邮件传输协议(SMTP)是一种协议&#xff0c;用于在邮件服务器之间发送电子邮件和路由电子邮件。Python提供smtplib模块&#xff0c;该模块定义了一个SMTP客户端会话对象&#xff0c;可用于使用SMTP或ESMTP侦听器守护程序向任何互联网机器发送邮件。 SMTP通讯的基本流程可以概…

C语言中#if,#if defined ,#ifdef,extern的用法描述

1、#if 和#ifdef当asd_eee表达式存在而且&#xff0c;值为ture的时候接续向下执行例如#define TARGET_LITTLE_ENDINA 1 #define TARGET_BIG_ENDINA 0 #ifdef TARGET_LITTLE_ENDINA call little endina function #else call big endina function #endif上面的今天写的代码&…

delphi报列表索引越界怎么处理_图解Elasticsearch索引机制,此篇带你领悟新世界...

前言随着Elastic的上市&#xff0c;ELK不仅在互联网大公司得到长足的发展&#xff0c;而且在各个中小公司都得到非常广泛的应用&#xff0c;甚至连"婚庆网站"都开始使用Elasticsearch了。随之而来的是 Elasticsearch 相关部署、框架、性能优化的文章早已铺天盖地。因…

为什么C语言函数不能返回数组,却可以返回结构体

C语言函数为什么不能返回数组&#xff1f;在C语言程序开发中&#xff0c;我们不可以编写下面这样的代码&#xff1a;char f(void[8]{ char ret;// ...fill... return ret; }int main(int argc, char ** argv) {char obj_a[10];obj_a f(); }不可以编写这样的代码这其实就是不能…

C语言printf()函数具体解释和安全隐患

程序员都知道&#xff0c;也都会使用printf函数&#xff0c;但你知道它也有“安全隐患”吗&#xff1f;下面就来举例我说说&#xff1a;嵌入式专栏1问题描述打印输出的数据并不是理论值&#xff0c;如下图&#xff08;右边&#xff09;&#xff1a;嵌入式专栏2进一步描述问题请…

java map 如何根据key获得对象_ThreadLocal:Java中的影分身

关于ThreadLocal&#xff0c;你有哪些疑问&#xff1f;ThreadLocal是用来解决什么问题的&#xff1f;如何使用ThreadLocal&#xff1f;ThreadLocal的实现原理是什么&#xff1f;可否举几个实际项目中使用ThreadLocal的案例&#xff1f;基础知识ThreadLocal是线程局部变量&#…

【C语言】你可能对 sizeof() 有点误解。。。

各位&#xff0c;今天还是按照惯例给大家分享一个C语言容易出现的小错误&#xff0c;这也是跟sizeof有关的&#xff0c;问题虽小&#xff0c;却可管中窥豹&#xff0c;话不多说&#xff0c;代码先行&#xff1a;#include int main() { int i; i 8; printf("%d\…

nginx tcp转发_Nginx学习(九):负载均衡服务

介绍对于请求而言&#xff0c;负载均衡能很好的均摊请求&#xff0c;提高服务端吞吐率和整体性能&#xff0c;多个服务节点部署的方式&#xff0c;也提高了容灾和服务高可用。一、负载均衡分类负载均衡分为&#xff1a;GSLB和SLB。1. GDLB全局负载均衡&#xff0c;往往按照国家…

控制台发送get命令_.NET Core使用命令行参数库构建控制台应用程序

前言在我们开发中可能需要设计一次性应用程序&#xff0c;这些实用程序可以利用接近原始源代码的优势&#xff0c;但可以在与主Web应用程序完全独立的安全性上下文中启动。具体在 [管理过程](https://12factor.net/admin-processes)中也已经列出了原因。创建控制台应用打开命令…