Java 并发编程和可伸缩性(三)

避免热点域

比如一个hashmap的size。优化的方法就是加一个计数器。但是对于ConcurrentHashMap来说。当并发的对其进行操作时,每次put和remove都需要改变这个计数器。所以在这个类里这个计数器就被叫做热点域,是可伸缩性的瓶颈。所以这里采取避免热点域的方法是对于应用锁分段的散列桶,ConcurrentHashMap中的size将对每个分段进行枚举并将每个分段的元素数量相加。并通过每个分段的锁来维护这个值。

替代独占锁的方法

独占锁对性能的影响还是比较大的,不是所有同步的需求都必须使用独占锁来实现。

ReadWriteLock实现了一种在多读取操作以及单个写入操作情况下的加锁规则。

原子变量提供了一种降低更新热点域时的开销。例如静态计数器,序列发生器,或者对链表数据结构中头节点的引用。

监测CPU利用率

如果测试时,cpu没能充分发挥性能,那么就不能得到相对准确的结果,如果cpu没有得到充分利用需要找出其中的原因,通常是以下几种:

  • 负载不充足
  • I/O密集
  • 外部限制(数据库或web服务)
  • 锁竞争

向对象池说不

在早期的JVM中,对象的分配和垃圾回收的执行速度非常慢,所以许多开发人员使用对象池技术来解决这一问题。如今java中的对象内存分配速度已经比C语言还快了。所以这项技术如果你还在使用,并且应用在并发的程序中,已经可以考虑是不是应该放弃它了。因为它带来的锁竞争问题的开销远大于对象分配的开销。不过在J2ME或者RTSJ,需要对象池技术来提高内存管理或响应性管理的效率。

减少上下文的开销

第一点就是减少锁的持有时间。因为持有时间越短那么发生竞争的情况就越少,上下文切换的次数就少。反应在程序就是请求服务的时间不宜过长。

减少锁竞争的来源。比如通过把I/O操作从处理请求的线程转移到一个专门的线程。

时间: 2024-10-24 01:15:48

Java 并发编程和可伸缩性(三)的相关文章

《Java并发编程实战》第三章 对象的共享 读书笔记

一.可见性 什么是可见性? Java线程安全须要防止某个线程正在使用对象状态而还有一个线程在同一时候改动该状态,并且须要确保当一个线程改动了对象的状态后,其它线程能够看到发生的状态变化. 后者就是可见性的描写叙述即多线程能够实时获取其它线程改动后的状态. *** 待补充   两个工人同一时候记录生产产品总数问题 1. 失效数据 可见性出现故障就是其它线程没有获取到改动后的状态,更直观的描写叙述就是其它线程获取到的数据是失效数据. 2. 非原子64位操作 3. 加锁与可见性 比如在一个变量的读取与

【Java并发编程实战】—– AQS(三):阻塞、唤醒:LockSupport

在上篇博客([Java并发编程实战]-– AQS(二):获取锁.释放锁)中提到,当一个线程加入到CLH队列中时,如果不是头节点是需要判断该节点是否需要挂起:在释放锁后,需要唤醒该线程的继任节点 lock方法,在调用acquireQueued(): if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; 在acquireQueued()中调用parkAndCheckIn

Java 并发编程和可伸缩性(二)

阻塞 非竞争的同步可以完全在JVM中处理,竞争的同步则可能需要操作系统的介入,从而增加开销.当在锁上发生竞争时,竞争失败的线程肯定会阻塞.JVM在实现阻塞行为时,可以采用自旋等待,就是通过循环不断的尝试获取锁.直到成功.或者通过操作系统挂起被阻塞的线程.这两种方式的效率高低取决于上下文切换的开销以及在成功获取锁之前需要等待的时间.如果等待时间较短,采用自旋等待的方式,如果时间较长,则采用操作系统挂起的方式.有些JVM将根据历史等待时间的分析数据在这两者之间进行选择,但是大多数JVM在等待锁时都只

Java 并发编程之测试(三)

产生更多的交替操作 由于并发代码中发生的错误一般都是低概率事件,所以在测试并发错误时需要反复地执行许多次,但有些方法可以提高发现这些错误的概率 ,在前面提到过,在多处理器系统上,如果 处理器的数量少于活动线程的数量,那么 与单处理器的系统 或者 包含多个处理器的系统相比,将能产生更多的交替行为. 有一种有用的方法能提高交替操作的数量.以便能更有效的搜索程序的状态空间:就是在访问状态的操作中加上Thread.yield作为一个空操作.当代码在访问状态的时候没有使用足够的同步,将存在一些对执行时序敏

Java并发编程原理与实战三十七:线程池的原理与使用

一.简介 线程池在我们的高并发环境下,实际应用是非常多的!!适用频率非常高! 有过使用过Executors框架的朋友,可能不太知道底层的实现,这里就是讲Executors是由ThreadPoolExecutor实现的.好的,让我们来看看ThreadPollExcutor是怎样实现的呢? 如果你想了解ThreadPoolExecutor的话.可以先从它的构造方法看起. ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime,

java并发编程的艺术(三)---lock源码

jdk1.5以后,并发包中新增了lock接口, 它相对于synchronized,多了以下三个主要特性:尝试非阻塞地获取锁(尝试获取锁成功则持有).能被中断地获取锁(锁的进程能响应中断).超时获取锁(指定时间截止之前获取锁). 我们看看它接口中定义的api: 获取锁 可中断地获取锁 尝试非阻塞地获取锁,能够获取则返回true,否则false 超时获取锁,三种返回情况:1.当前线程在超时时间内获得了锁.2.当前线程在超时时间内被中断.3.超时时间内没获得锁 释放锁 获取等待通知组件,该组件和当前的

《Java并发编程实战》第十一章 性能与可伸缩性 读书笔记

造成开销的操作包括: 1. 线程之间的协调(例如:锁.触发信号以及内存同步等) 2. 增加的上下文切换 3. 线程的创建和销毁 4. 线程的调度 一.对性能的思考 1 性能与可伸缩性 运行速度涉及以下两个指标: 某个指定的任务单元需要"多快"才能处理完成.计算资源一定的情况下,能完成"多少"工作. 可伸缩性: 当增加计算资源时(例如:CPU.内存.存储容器或I/O带宽),程序的吞吐量或者处理能力能相应地增加. 2 评估各种性能权衡因素 避免不成熟的优化.首先使程序正

<java并发编程的艺术>读书笔记-第三章java内存模型(一)

一概述 本文属于<java并发编程的艺术>读书笔记系列,继续第三章java内存模型. 二重排序 2.1数据依赖性 如果两个操作访问同一个变量,且这两个操作中有一个为写操作,此时这两个操作之间就存在数据依赖性.数据依赖分下列三种类型: 名称 代码示例 说明 写后读 a = 1;b = a; 写一个变量之后,再读这个位置. 写后写 a = 1;a = 2; 写一个变量之后,再写这个变量. 读后写 a = b;b = 1; 读一个变量之后,再写这个变量. 上面三种情况,只要重排序两个操作的执行顺序,

Java并发编程(三)volatile域

相关文章 Java并发编程(一)线程定义.状态和属性 Java并发编程(二)同步 Android多线程(一)线程池 Android多线程(二)AsyncTask源代码分析 前言 有时仅仅为了读写一个或者两个实例域就使用同步的话,显得开销过大,volatile关键字为实例域的同步訪问提供了免锁的机制.假设声明一个域为volatile,那么编译器和虚拟机就知道该域是可能被还有一个线程并发更新的. 再讲到volatile关键字之前我们须要了解一下内存模型的相关概念以及并发编程中的三个特性:原子性,可见