Java 多线程(四)性能与可伸缩性

对性能的思考

提升性能意味着用更少的资源做更多的事情。 “资源”的含义很广。对于给定的操作,通常会缺乏某种特定的资源,例如CPU时钟周期、内存、网路带宽、IO带宽、数据库请求、磁盘空间以及其它资源。当操作性能由于某种特定资源而受到限制时,我们通常将该操作称为资源密集型的操作,例如CPU密集型、数据库密集型等。

使用多线程的目的是提升整体性能,但与单线程的方法比,使用多线程总会引起一些额外的开销。照成这些开销的操作包括:线程之间的协调(例如加锁、触发信号以及内存同步等),增加的上下文切换,线程的创建和销毁,以及线程的调度等。

想要通过并发获得更好的性能,需要努力做好两件事情:更有效地利用现有处理资源;以及在出现新的处理资源时使程序尽可能地利用这些新资源。

性能与可伸缩性

可伸缩性:当增加计算资源时(CPU、内存、储存容量、IO带宽),程序的吞吐量或者处理能力能相应的增加

避免不成熟的优化。首先使程序正确,然后再提高运行速度(如果它还运行的不够快)

大多数性能决策中都包含多个变量,并且非常依赖于运行环境。在使某个方案比其他方案“更快”之前,请先搞清楚:

  • “更快”的含义是什么?
  • 该方法在什么条件下运行得更快?低负载还是高负载?大数据集还是小数据集?能否测试验证?
  • 在实现这种性能提升时需要付出哪些隐含的代价,增加开发风险或维护开销?这种权衡是否合适?

Amdahl 定律

在增加计算资源的情况下,程序在理论上能够实现最高的加速比,这个值取决于程序中可并行组件与串行组件所占的比重。

假定F是必须被串行执行的部分,那么根据Amdahl定律,在包含N个处理器的机器中,最高的加速比为:

Speedup = 1/(F+(1-F)/N)

当N趋近于无穷大时,最大的加速比趋近于1/F。

在所有并发程序中都包含一些串行部分。如果你认为你的程序中不存在串行部分,那么可以再仔细检查一遍。

线程引入的开销

单线程程序既不存在线程调度,也不存在同步开销,而且不需要使用锁来保证数据结构的一致性。在多个线程的调度和协调过程中都需要一定的性能开销:对于为了提升性能而引入的线程来说,并行带来的性能提升必须超过并发导致的开销。

上下文切换

如果可运行的线程数大于CPU的数量,那么操作系统最终会将某个正在运行的线程调度出来,从而使其他线程能够使用CPU。这将导致一次上下文切换。这个过程中将保存当前运行线程的执行上下文,并将新调度近来的线程的执行上下文设置为当前上下文

上下文切换的实际开销会随着平台的不同而变化:在大多数通用的处理器中,上下文切换的开销相当于5000~10000个时钟周期,也就是几微秒。

内存同步

同步操作的性能开销包括多个方面。再 synchronizedvolatile提供的可见性保证中可能会用到一些特殊指令,即内存栅栏。内存栅栏可以刷新缓存,使缓存无效,刷新硬件的写缓冲,以及停止执行管道。内存栅栏可能会对性能带来间接的影响,因为它们将抑制一些编译器的优化操作。在内存栅栏种,大多数操作是不能被重排序的。

不要过度担心非竞争同步带来的开销。JVM基本的机制已经非常快了,并且JVM还能进行额外的优化以进一步降低或消除开销。因此我们应该将优化的重点放在那些发生锁竞争的地方。

阻塞

非竞争的同步完全可以在JVM中进行处理,而竞争的同步可能需要操作系统的介入,从而增加开销。当在锁上发生竞争时,竞争失败的线程肯定会阻塞。JVM在实现阻塞行为时,可以采用自旋等待(Spin-waiting,指通过循环不断地尝试获取锁,知道成功)或者通过操作系统挂起被阻塞的线程。这两种方式的效率高低,要取决于上下文切换的开销以及在成功获取锁之前需要等待的时间。

如果等待时间短,则适合采用自旋方式等待,如果时间较长,则适合采用线程挂起方式。
当线程被挂起的过程中将包含两次额外的上下文切换,以及所有必要的操作系统操作和缓存操作。

减少锁的竞争

串行会降低可伸缩性,上下文切换也会降低性能。在锁上发生竞争将同时导致这两种问题。在并发程序中,对可伸缩性的主要威胁就是独占方式的资源锁。

有两个因素将影响在锁上发生竞争的可能性:锁的请求频率,以及每次持有该锁的时间。

有三种方式可以降低锁的竞争程度:
* 减少锁的持有时间
* 降低锁的请求频率
* 使用带有协调机制的独占锁,这些机制允许更高的并发性。(乐观锁????)

缩小锁的范围

降低发生竞争的可能性的一种有效方式就是尽可能缩短锁的持有时间。例如:移除同步代码块中与锁无关的代码。

尽管缩小同步代码块能提高伸缩性,单同步代码块不宜过小----一些需要原子操作的多个操作必须包含在一个同步块中。
在分解同步代码块时,理想的平衡点于平台相关。
此外,如果JVM执行粗粒度化操作,那么可能会将分解的同步代码块又重新合并起来。

减小锁的粒度

另一种减小锁的持有时间的方式是降低线程请求所的频率(从而减小发生竞争的可能性)。这可以通过分解锁和锁分段技术来实现,采用多个相互独立的锁来保护独立的状态变量,从而改变这些变量在之前由单个锁来保护的情况。这些技术能减少锁操作的粒度,并能实现更到的可伸缩性,然而使用的锁越多,那么发生死锁的风险也就越高。

由于使用线程常常是为了充分利用多个处理器的计算能力,因此在并发程序中关注的更多的是吞吐量和可伸缩性,而不是服务时间。Amdahl定律告诉我们,程序的可伸缩性取决于代码中必须串行执行的代码比例。因为Java程序中串行操作的主要来源是独占方式的资源锁,因此我们可以通过以下方式来提升可伸缩性:减少锁的持有时间、降低锁的粒度、采用非独占的锁或非阻塞的锁来替代独占锁。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342