frangi黑森矩阵

韦伯斯特今年的新单词或最常用单词列表中可能找不到“ Heisenbug”一词。 不幸的是,作为软件工程师,我们对这种险恶的生物太熟悉了。 “海森堡不确定性原理”的双关语-一种量子物理学概念,认为观察者仅通过观察行为就能影响他们所观察的事物-术语“ Heisenbug”被嬉戏地分配给计算机错误,该错误在生产中的期望值最低时出现,但是连接乐器时所有尝试重现的声音都变空。 结果,几乎不可能同时重现潜在环境和错误本身。

但是,有一种万无一失的方式来产生heisenbug:
(1)编写并发程序,并确保忽略并发概念,例如发布,转义,Java内存模型和字节码重新排序。
(2)彻底测试程序。 (不用担心,所有测试都会通过!)
(3)将程序发布到生产中。
(4)等待下一次生产变更冻结,然后检查您的收件箱。 立刻,您会发现大量的烦人的电子邮件,这对您和您的错误应用程序都造成了打击。

在解决如何避免此类heisenbug之前,一个更基本的问题可能更合适:如果编程并发是如此困难,那么为什么还要这么做呢? 实际上,有很多原因要打扰:

并行性 -随着处理器和内核数量的增加,多线程允许程序利用并行性来确保我们的程序不会使一个内核超负荷,而其他内核却无所适从。 即使是在一台单核计算机上,可能不忙于执行计算(可能因为它们受I / O限制或正在等待其他子系统)的应用程序也会有空闲的处理器周期。 并发使程序可以使用这些空闲周期来优化性能。
公平性 -如果两个或多个客户端正在等待对子系统的访问,则不希望让每个客户端在执行之前都等待前一个客户端完成。 并发允许我们的程序将每个客户端请求分配给一个线程,从而最大程度地减少感知到的延迟。
便利 -编写一系列单独运行的小任务通常比创建和协调大型程序来管理所有这些任务要容易。

但是,这些原因并不能减轻并发编程困难的事实。 如果我们的程序员在我们的应用程序中没有充分考虑它,那么我们将面临严重的heisenbug。 在本文中,我们将介绍在Java中设计或开发并发应用程序时要记住的十个技巧。

提示1-通知自己

我建议您通读Brian Goetz的《 Java Concurrency in Practice》。 这个2006年最畅销的经典著作从头开始涵盖了Java并发性。 我第一次读这本书(您会发现自己一遍又一遍地引用它),我想起了我多年来犯下的所有违规行为,在肚子里挖了一个坑。 要深入了解Java并发的各个方面,请以Java专家Heinz Kabutz博士所创建,并由Brian Goetz认可的《 Java并发实践》一书为基础,考虑参加并发专家培训 。

技巧2-使用可用的专家资源

使用Java 5中引入的java.util.concurrent程序包。如果您对该程序包中的所有组件没有丰富的经验,建议您从SourceForge下载自执行的Java Concurrent Animated应用程序,其中包含一系列内容。动画(由您自己写的)说明了每个并发包组件。 该下载内容是一个交互式目录,在构建自己的并发解决方案时可以参考该目录。

当Java在1995年底首次出现时,它是最早包含多线程作为核心语言功能的编程语言之一。 因为并发非常困难,所以我们发现一些优秀的程序员编写了一些非常差的并发程序。 此后不久,奥斯威戈州立大学的并发专家Doug Lea教授发表了他的巨著,《 Java并发编程》。 现在,在第二版中,这项工作引入了并发设计模式的目录,这些目录成为了java.util.concurrent包的基础。 Doug Lea告诉我们,以前可能已内联到类中的并发代码可以提取到可以由并发专家认证的单个组件中,从而使我们可以专注于程序逻辑,而不会对并发的繁琐工作感到困惑。 通过使自己熟悉该程序包并在程序中使用它,我们可以大大降低引入并发错误的风险。

秘诀3-注意陷阱

并发不是高级语言功能,因此暂时不要认为您的程序不受线程安全问题的影响。 所有Java程序都是多线程的:JVM不仅创建诸如垃圾收集器,终结器和关闭挂钩之类 的线程 ,而且其他 框架也 引入了它们自己的线程。 例如,Swing和抽象窗口工具包(AWT)引入了事件调度线程。 远程方法调用(RMI)Java应用程序编程接口以及所谓的模型视图控制器(MVC)Web框架(例如Struts和Spring MVC)会在每次调用时引入线程,因此特别容易受到攻击。 所有这些线程都可以调用代码中的编程钩子,从而有可能修改应用程序的状态。 如果在没有适当同步的情况下允许多个线程在读取或写入操作中访问程序状态变量,则您的程序有问题。 请注意这一点,并积极进行并发编码。

提示4-采取直接的方法

首先为正确性编写代码,然后为性能进行纠正。 诸如封装之类的技术可以使您的程序具有线程安全性,但有可能减慢其运行速度。 尽管如此,HotSpot通常会在优化这些方面做得很好,因此,最好的做法是首先确保您的程序正确运行,而后再担心性能调整。 我们常常发现,巧妙的优化使我们的代码难以理解和维护,只是没有及时节省材料。 无论看起来多么优雅或聪明,都应避免此类优化。 通常,建议编写未优化的代码,然后使用探查器查找热点和瓶颈并尝试纠正这些问题。 首先瞄准最大的瓶颈,然后重新配置; 您会经常发现,通过更正一个热点,您可以自动更正一些下一个最严重的热点。 从一开始就正确地编写程序要比以后为安全起见进行改进要容易一个数量级,因此请使其保持简单,正确并从一开始就认真记录所有并发假设。 使用并发注释(例如@ ThreadSafe,@ NotThreadSafe,@ Immutable和@GuardedBy)可以在将并发假设记录到您的代码中提供很大的帮助。

秘诀5-掌握原子性

在并发编程中,如果一个操作或一组操作在系统的其余部分看来是在调用和响应之间的单个瞬间发生的,则它是原子的。 原子性是与并发进程隔离的保证。 因为Java确保32位操作是原子操作,所以设置整数和浮点数将始终至少确保“空中”的安全性。 通过保证同时设置一个值的多个线程生成这些值之一,而不是某些突变的混合值“凭空”,可以实现空中保护。 但是,涉及长双精度变量的64位运算不提供这种保证; Java语言规范允许以非原子方式将一个64位集视为两个32位集。 结果,当使用长整型和双整型变量时,尝试同时修改变量的线程可能会产生意外和不可预测的结果,因为变量的最终值可能不是第一个线程或第二个线程都未设置的,而是两者设置的字节的某种组合。 例如,如果线程A设置十六进制值1111AAAA,线程B设置值2222BBBB,则结果值很可能是混合变量1111BBBB,两者均未设置。 通过将此类变量声明为volatile可以很容易地纠正此问题,这告诉运行时原子地处理setter。 但是,不稳定的声明不是万能药。 尽管它确保设置器是原子的,但需要进一步同步以确保对变量的其他操作是原子的。 例如,如果“ count”是一个长变量,则完全有可能丢失对递增函数count ++的调用。 这是由于以下事实:尽管看起来是单个操作,但++实际上是三个操作查找,增量和设置的组合。 如果并发线程以不正确的交织顺序执行操作,则两个线程可能会同时执行查找操作并获得相同的值,从而导致增量操作产生相同的结果,结果是set操作将发生冲突。 ,也产生相同的价值。 如果“计数”是命中计数器,则命中丢失。 当多个线程可以同时执行检查,更新和设置操作时,请确保在同步块中执行它们,或使用诸如ReentrantLock之类的java.util.Lock实现。

许多程序员误以为只有在写入值时才需要同步,而在读取值时才需要同步。 这是一个误解。 例如,如果前者是同步的,而后者没有同步,则读取器线程很可能看不到写入器线程写入的值。 尽管这似乎是一个错误,但实际上它是Java的重要功能。 线程通常在不同的CPU或内核上执行,并且处理器的设计使得将数据从一个内核转移到另一个内核并不是一项快速的操作。 Java意识到了这一点,因此在启动线程时默认允许每个线程抽象其状态的副本。 随后可以在不同线程中发生的最近的非限定状态更改之后访问原始状态。 尽管将变量声明为volatile将保证可见性,但仍不能保证原子性。 通过在必要时进行适当的同步,您既有权力(也有义务)对此进行编码。

技巧6-限制线程

防止线程相互竞争访问共享数据的一种方法是不共享! 如果特定数据点仅由单个线程访问,则无需考虑其他同步。 此技术称为“线程限制”。

限制线程的一种方法是使对象不可变。 尽管就生产的物体数量而言,不可变的物体价格昂贵,但有时它们正是医生在维护方面所订购的。

技巧7-尊重Java内存模型

请注意Java内存模型协定的可变可见性。 所谓的“程序顺序规则”指出,在线程中设置变量后,从那个时间开始,该变量对于该线程仍然可见,而与任何同步无关。 当线程A依靠对M的内在锁通过对sync(M)的调用而获得该锁并且随后该锁被线程B释放并获取时,线程A在释放该锁之前设置的所有内容-包括(可能意外地) 获得锁之前设置的那些变量-在线程B获得M的锁之后将对线程B可见。释放锁的作用是一种对线程可见的任何值的一种内存提交。 (请参见下图)。 请注意,java.util.concurrent中的新锁定组件Semaphore和ReentrantLock表现出相同的语义。 易失性变量也具有类似的动态特性:当线程A向易失性变量X写入数据时,它类似于退出同步块; 当线程B读取该变量X时,这类似于在同一变量上输入一个同步块,这意味着线程A写入X时可见的所有内容在读取X之后对线程B都是可见的。

秘诀8-线程数

确定多少线程适合您的应用程序。 可以使用以下变量开发公式来正式执行此计算:
令T为我们要推导的理想线程数
令C为CPU数
令X为每个进程使用的利用率百分比
令U为目标利用率百分比。

如果我们只有一个100%使用的CPU,那么我们只需要一个线程。 当利用率为100%时,该规则指定线程数应等于CPU数,如公式T = C所示。 但是,如果我们发现每个CPU的利用率仅为x的百分比,则可以通过将CPU的数量除以x来增加线程的数量,从而得出T = C / x。 例如,如果x是.5,那么我们可以拥有的线程数是CPU的两倍,这将在所有线程上产生100%的利用率。 如果仅想产生目标利用率U%,则必须乘以U,因此公式变为T = UC / x。 最后,如果我们将线程视为p%受处理器限制(即计算),而n%无处理器受限制(即等待),则显然x = p /(n + p)。 (请注意,n%+ p%= 100%)。 将这些变量代入上式可得出以下结果:

为了确定p和n,您可以在每个CPU和非CPU调用(例如I / O和JDBC调用)之前和之后为线程配备时间日志,然后分析它们的相对时间。 当然,并非所有线程都将显示相等的指标,因此您必须取平均值。 尽管如此,这应该给您一个很好的经验法则来开始配置调整。 正确实现这一点很重要,因为引入太多线程实际上会降低性能。 保持线程数量可配置也很重要,以便在调整硬件配置时可以轻松地调整线程数。 得出线程计数的值后,将该值传递给FixedThreadPoolExecutor。 由于操作系统负责进程之间的上下文切换,因此请将U%设置得足够低以容纳其他进程。 但是,对于此公式中的其余计算,只应考虑自己的过程。

好消息是,有一些摆动,因此如果您将线程计数错误最多提高了20%左右,则可能不会对性能造成重大影响。

技巧9-在服务器模式下进行开发

即使您正在开发客户端应用程序,也要以服务器模式执行开发。 服务器模式标志充当运行时环境的提示,它可以执行某些字节码重新排序以实现性能优化。 尽管在服务器模式下这种编译器优化比在客户端模式下更频繁地发生,但仍然可以在客户端模式下找到它们。 使用服务器模式可以在测试过程中尽早公开此类优化,而不必等待生产发布。

但是请使用-server,-client和-Xcomp进行测试(以消除运行时分析,预先执行最大优化。)

要设置服务器模式,调用Java与- 服务器交换机。 我的Java专家通讯和Java硕士课程的成名朋友Heinz Kabutz博士指出,某些处于64位模式的JVM(例如直到最近的Apple OSX)将忽略- 服务器切换,因此您可能还想包括-showversion开关,它将在程序日志的顶部显示版本,包括客户端或服务器模式。 如果使用的是Apple OSX,则可以使用-d32开关切换到32位模式。 简而言之,请使用-showversion -d32 -server进行测试,并通常检查日志以确保您正在使用所需的Java版本。 同样,Heinz博士建议在执行测试时,使用单独的测试在-Xcomp(删除JIT运行时优化)和-Xmixed(默认为-Xmixed,优化热点)之间切换。

技巧10-测试并发代码

我们都知道如何构建单元测试器来测试代码中方法调用的后置条件。 多年来,这已经成为我们习惯于执行的困难例程,因为我们了解从开发到维护的过程中节省的时间价值。 但是,我们可以测试的编程最重要的方面是并发性。 这是因为它代表了最脆弱的代码,也是我们可以期望找到大多数错误的地方。 具有讽刺意味的是,在这方面,我们往往最无所作为,主要是因为并发测试器太难生产了。 这归因于线程交织的不确定性-通常无法预测哪个线程将以哪个顺序执行,因为这些线程会因机器而异,甚至同一机器上的进程也有所不同。

测试并发的一种技术是使用并发组件本身来生成测试。 例如,要模拟任意线程的时间安排,请考虑对线程进行排序的各种可能性,并使用调度的执行程序根据这些可能性确定性地对线程进行排序。 在测试并发性时,请记住,当线程引发异常时,它不会导致JUnit失败。 JUnit仅在主线程遇到异常时才会失败,而在其他线程遇到异常时则不会。 解决此问题的一种方法是使用FutureTask指定您的并发任务,而不是Runnable 。 当FutureTask实现实现Runnable时 ,可以将其提交给预定的执行程序。 FutureTask可以接受Callable或Runnable作为构造函数参数。 CallableRunnable相似,但有两个明显的区别: Callable返回一个值,而Runnable返回void, Callable引发已检查的ExecutionException,而Runnable只能引发未检查的异常。 ExecutionException具有getCause方法,该方法将返回触发它的实际异常。 (请注意,如果您传递了Runnable,则还必须传递一个返回对象。FutureTask将内部将Runnable和return对象包装在Callable中,因此您仍将拥有Callable的好处。)通过使用FutureTask,您可以计划并发代码,然后让JUnit主线程调用获取结果。 get方法旨在重新抛出由其CallableRunnable异步引发的任何异常。

但是,仍然存在挑战。 假设您有一个预期会阻塞的方法,并且您想测试该方法是否确实能完成预期的工作。 您如何测试该区块? 您要等多久才能确定该方法实际上已阻止? FindBugs将查找一些并发问题,您可能需要考虑使用并发测试框架。 FindBugs的合著者Bill Pugh共同创建的MultithreadedTC提供了一个API,用于指定和测试所有交错的变体以及其他并发特定功能。

测试时要记住的重要一点-Heisenbug不会在每次运行时都抬起头来,因此结果不是简单的是/不是答案。 并发测试您的并发测试器数以千计,并获得有关平均值和标准偏差的统计指标,以衡量您的成功。

简而言之

Brian Goetz警告说,随着芯片设计人员转向较弱的内存模型,并且内核数量的增加带来了更多的交叉,可见性故障将随着时间的流逝而变得越来越普遍。

因此,我们不惜一切代价避免将并发视为理所当然。 让我们了解Java内存模型的内部工作原理,并尽可能多地使用java.util.concurrent来消除并发程序中的heisenbug,从而使我们的收件箱中的电子邮件免费,除了满足和赞美之外,什么都不包含。

链接:

Java并发实践
Java专家通讯
并发专家课程
Java并发动画

翻译自: https://www.infoq.com/articles/exterminating-heisenbugs/?topicPageSponsorship=c1246725-b0a7-43a6-9ef9-68102c8d48e1

frangi黑森矩阵

frangi黑森矩阵_消灭黑森臭虫相关推荐

  1. 多目标分类的混淆矩阵_用于目标检测的混淆矩阵

    多目标分类的混淆矩阵 After training a machine learning classifier, the next step is to evaluate its performanc ...

  2. 三维错切变换矩阵_图像的仿射变换

    目录: 概述 图像基本变换 仿射变换 原理 python实现 一.概述 图像的几何变换主要包括:平移.缩放.旋转.仿射.透视等等.图像变换是建立在矩阵运算基础上的,通过矩阵运算可以很快的找到不同图像的 ...

  3. python读取文档中有很多指标的数据 写成矩阵_图像处理与特征提取 —— 从 MATLAB 到 Python(一)图像、矩阵与数据的读写...

    最近几个实验从 MATLAB 环境转入到 Python 环境做,踩了几个小坑,记录一下. 写一半发现太长,分开几篇写,计划如下:基本图像处理 特征提取 分类 距离.度量.评价指标与效果评估,及相关绘图 ...

  4. python n个list如何组成矩阵_学完Python,我决定熬夜整理这篇总结...

    来源:http://suo.im/5wzRqt 前言:学完Python,我决定熬夜整理这篇总结- 一.了解Python Python之父 Guido Van Rossum,一位荷兰程序员,在1989年 ...

  5. matlab 创建同型矩阵_仅在第1部分之间的海洋中探索同型图

    matlab 创建同型矩阵 同型的教训-第1部分 (Lessons of Isotype - PART 1) There's much to be told in the story of the I ...

  6. r语言重复向量变矩阵_游戏如何使重复变得有趣

    r语言重复向量变矩阵 Have you ever stopped to consider that in most video games, you're doing variations of th ...

  7. 数学_计算协方差矩阵/信息矩阵_理论+例子

    目录 1. 多元高斯分布 1.1 标准高斯分布 1.2 一元高斯函数(一元高斯分布概率密度) 1.3 多元高斯分布 2. 协方差矩阵的计算 2.1 问题定义 2.2 室内外温度的例子 参考: 1. 多 ...

  8. seurat提取表达矩阵_单细胞分析实录(5): Seurat标准流程

    前面我们已经学习了单细胞转录组分析的:使用Cell Ranger得到表达矩阵和doublet检测,今天我们开始Seurat标准流程的学习.这一部分的内容,网上有很多帖子,基本上都是把Seurat官网P ...

  9. matlab 创建同型矩阵_探索同型图我们的私人生活第2部分

    matlab 创建同型矩阵 同型的教训–第2部分 (Lessons of Isotype - PART 2) There's much to be told in the story of the I ...

最新文章

  1. div css图片列表实例
  2. js操作样式自动prefix
  3. python利用自动识别写模块_教你用Python 实现自动导入缺失的库
  4. Python 可以满足你任何 API 使用需求
  5. RabbitMQ学习之Flow Control
  6. 【CSAPP笔记】11. 存储器层次结构
  7. 基于vue与element ui的vue-cron插件的使用及将定时任务cron表达式解析成中文
  8. 收藏模板:软件日报告模板(参考)
  9. U盘Linux游戏系统,batocera.linux U盘街机游戏系统下载
  10. 大版本号跨越,AIDA64更新6.0版本:更新测试,支持Zen 2架构
  11. Egyptian Collegiate Programming Contest (ECPC 2015)
  12. 新东方雅思词汇---8.1、reckon
  13. ppt 转html乱码,ppt转换成pdf乱码解决方法.pdf
  14. 初识C语言(三)--最终章,万字解析,趣味讲解完C语言的最后知识点
  15. 云学智慧校园-高校信息化一体化平台 V2.0 SE-WBS排期表(初拟)
  16. 如何破解PDF文档不能打印?
  17. 不同 frame 之间调用 js 函数
  18. Kubernetes 可扩展性简介
  19. linux18.04安装显卡驱动,Ubuntu18.04安装nvidia显卡驱动
  20. VMWare软件定义数据中心SDDC

热门文章

  1. 都2020年了,别再迷信啤酒与尿布!数据分析的真相在这
  2. c语言 不同的io口组成数组,本章内容并行IO口电路结构认识C语言C语言基本语句C语.ppt...
  3. 行列式(Determinants)
  4. java程序员 待遇_Java程序员之间薪资对比,为什么差距这么大?
  5. Android游戏开发教程------(绘制屏幕)
  6. Excel如何实现间隔插入空白行
  7. 获取比Administrator还高的权限——SYSTEM权限
  8. 计算机类期刊投稿心得
  9. html5做宠物饲养,说一说最适合上班族养的十大宠物
  10. 用“法外狂徒”理解C++中的引用