Java多线程性能优化
大家使用多线程无非是为了提高性能,但如果多线程使用不当,不但性能提升不明显,而且会使得资源消耗更大。下面列举一下可能会造成多线程性能问题的点:
死锁
过多串行化
过多锁竞争
切换上下文
内存同步
下面分别解析以上性能隐患
死锁
关于死锁,我们在学习操作系统的时候就知道它产生的原因和危害,这里就不从原理上去累述了,可以从下面的代码和图示重温一下死锁产生的原因:
[java] view plaincopy
public class LeftRightDeadlock {
private final Object left = new Object();
private final Object right = new Object();
public void leftRight() {
synchronized (left) {
synchronized (right) {
doSomething();
}
}
}
public void rightLeft() {
synchronized (right) {
synchronized (left) {
doSomethingElse();
}
}
}
}
预防和处理死锁的方法:
1)尽量不要在释放锁之前竞争其他锁
一般可以通过细化同步方法来实现,只在真正需要保护共享资源的地方去拿锁,并尽快释放锁,这样可以有效降低在同步方法里调用其他同步方法的情况
2)顺序索取锁资源
如果实在无法避免嵌套索取锁资源,则需要制定一个索取锁资源的策略,先规划好有哪些锁,然后各个线程按照一个顺序去索取,不要出现上面那个例子中不同顺序,这样就会有潜在的死锁问题
3)尝试定时锁
Java 5提供了更灵活的锁工具,可以显式地索取和释放锁。那么在索取锁的时候可以设定一个超时时间,如果超过这个时间还没索取到锁,则不会继续堵塞而是放弃此次任务,示例代码如下:
[java] view plaincopy
public boolean trySendOnSharedLine(String message,
long timeout, TimeUnit unit)
throws InterruptedException {
long nanosToLock = unit.toNanos(timeout)
- estimatedNanosToSend(message);
if (!lock.tryLock(nanosToLock, NANOSECONDS))
return false;
try {
return sendOnSharedLine(message);
} finally {
lock.unlock();
}
}
这样可以有效打破死锁条件。
4)检查死锁
JVM采用thread dump的方式来识别死锁的方式,可以通过操作系统的命令来向JVM发送thread dump的信号,这样可以查询哪些线程死锁。
过多串行化
用多线程实际上就是想并行地做事情,但这些事情由于某些依赖性必须串行工作,导致很多环节得串行化,这实际上很局限系统的可扩展性,就算加CPU加线程,但性能却没有线性增长。有个Amdahl定理可以说明这个问题:
其中,F是串行化比例,N是处理器数量,由上可知,只有尽可能减少串行化,才能最大化地提高可扩展能力。降低串行化的关键就是降低锁竞争,当很多并行任务挂在锁的获取上,就是串行化的表现
过多锁竞争
过多锁竞争的危害是不言而喻的,那么看看有哪些办法来降低锁竞争
1)缩小锁的范围
前面也谈到这一点,尽量缩小锁保护的范围,快进快出,因此尽量不要直接在方法上使用synchronized关键字,而只是在真正需要线程安全保护的地方使用
2)减小锁的粒度
Java 5提供了显式锁后,可以更为灵活的来保护共享变量。synchronized关键字(用在方法上)是默认把整个对象作为锁,实际上很多时候没有必要用这么大一个锁,这会导致这个类所有synchronized都得串行执行。可以根据真正需要保护的共享变量作为锁,也可以使用更为精细的策略,目的就是要在真正需要串行的时候串行,举一个例子:
[java] view plaincopy
public class StripedMap {
// Synchronization policy: buckets[n] guarded by locks[n%N_LOCKS]
private static final int N_LOCKS = 16;
private final Node[] buckets;
private final Object[] locks;
private static class Node { ... }
public StripedMap(int numBuckets) {
buckets = new Node[numBuckets];
locks = new Object[N_LOCKS];
for (int i = 0; i < N_LOCKS; i++)
locks[i] = new Object();
}
private final int hash(Object key) {
return Math.abs(key.hashCode() % buckets.length);
}
public Object get(Object key) {
int hash = hash(key);
synchronized (locks[hash % N_LOCKS]) {
for (Node m = buckets[hash]; m != null; m = m.next)
if (m.key.equals(key))
return m.value;
}
return null;
}
public void clear() {
for (int i = 0; i < buckets.length; i++) {
synchronized (locks[i % N_LOCKS]) {
buckets[i] = null;
}
}
}
...
}
上面这个例子是通过hash算法来把存取的值所对应的hash值来作为锁,这样就只需要对hash值相同的对象存取串行化,而不是像HashTable那样对任何对象任何操作都串行化。
3)减少共享资源的依赖
共享资源是竞争锁的源头,在多线程开发中尽量减少对共享资源的依赖,比如对象池的技术应该慎重考虑,新的JVM对新建对象以做了足够的优化,性能非常好,如果用对象池不但不能提高多少性能,反而会因为锁竞争导致降低线程的可并发性。
4)使用读写分离锁来替换独占锁
Java 5提供了一个读写分离锁(ReadWriteLock)来实现读-读并发,读-写串行,写-写串行的特性。这种方式更进一步提高了可并发性,因为有些场景大部分是读操作,因此没必要串行工作。关于ReadWriteLock的具体使用可以参加一下示例:
[java] view plaincopy
public class ReadWriteMap<K,V> {
private final Map<K,V> map;
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private final Lock r = lock.readLock();
private final Lock w = lock.writeLock();
public ReadWriteMap(Map<K,V> map) {
this.map = map;
}
public V put(K key, V value) {
w.lock();
try {
return map.put(key, value);
} finally {
w.unlock();
}
}
// Do the same for remove(), putAll(), clear()
public V get(Object key) {
r.lock();
try {
return map.get(key);
} finally {
r.unlock();
}
}
// Do the same for other read-only Map methods
}
切换上下文
线程比较多的时候,操作系统切换线程上下文的性能消耗是不能忽略的,在构建高性能web之路------web服务器长连接 可以看出在进程切换上的代价,当然线程会更轻量一些,不过道理是类似的
内存同步
当使用到synchronized、volatile或Lock的时候,都会为了保证可见性导致更多的内存同步,这就无法享受到JMM结构带来了性能优化。
Java多线程性能优化的更多相关文章
- 探索并发编程(六)------Java多线程性能优化
大家使用多线程无非是为了提高性能,但如果多线程使用不当,不但性能提升不明显,而且会使得资源消耗更大.下面列举一下可能会造成多线程性能问题的点: 死锁 过多串行化 过多锁竞争 切换上下文 内存同步 下面 ...
- Java程序性能优化技巧
Java程序性能优化技巧 多线程.集合.网络编程.内存优化.缓冲..spring.设计模式.软件工程.编程思想 1.生成对象时,合理分配空间和大小new ArrayList(100); 2.优化for ...
- Java的性能优化
http://www.toutiao.com/i6368345864624144897/?tt_from=mobile_qq&utm_campaign=client_share&app ...
- [JAVA] java程序性能优化
一.避免在循环条件中使用复杂表达式 在不做编译优化的情况下,在循环中,循环条件会被反复计算,如果不使用复杂表达式,而使循环条件值不变的话,程序将会运行的更快. 例子: import java.util ...
- 从虚拟机视角谈 Java 应用性能优化
从虚拟机视角谈 Java 应用性能优化 周 祥, 软件工程师, IBM 简介:Java 的普及和广泛应用,以及其基于虚拟机运行的机制,使得性能问题越来越重要.本文从 Java 虚拟机的角度,特别是垃圾 ...
- 《Java程序性能优化:让你的Java程序更快、更稳定》
Java程序性能优化:让你的Java程序更快.更稳定, 卓越网更便宜,不错的书吧
- Java GC 专家系列5:Java应用性能优化的原则
本文是GC专家系列中的第五篇.在第一篇理解Java垃圾回收中我们学习了几种不同的GC算法的处理过程,GC的工作方式,新生代与老年代的区别.所以,你应该已经了解了JDK 7中的5种GC类型,以及每种GC ...
- Java 的性能优化
jvm 中的方法区: 永久区---存的类的信息.方法.常量 .静态变量 1.Java的性能优化 1.减少gc的压力,优先级比较低的线程,他是一个守护线程 回收我们的堆内存. 2.尽量的避免我们的new ...
- 35 个 Java 代码性能优化总结
前言 代码优化,一个很重要的课题.可能有些人觉得没用,一些细小的地方有什么好修改的,改与不改对于代码的运行效率有什么影响呢?这个问题我是这么考虑的,就像大海里面的鲸鱼一样,它吃一条小虾米有用吗?没用, ...
随机推荐
- 在Linux上部署和操作Couchbase
couchbase属于nosql系列,个人感觉它要比mongodb操作简单,mongo的查询语句太复杂.在数据的持久性方面它区别于其他nosql 的唯一大亮点是不受限于其内存分配了多少,只要磁盘空间够 ...
- dapper.rainbow
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.D ...
- 升级python版本导致Django无法使用的解决办法
运行环境是CentOS6.2 x86_64,在把python从2.6.6升级到2.7.5后,由于环境变量的改变,在python代码中再import django的话将会出现以下报错: “No mo ...
- flex直接访问服务器
1.读取指定文件里的xml: <?xml version="1.0" encoding="utf-8"?><s:WindowedApplica ...
- easy ui datagrid 数据绑定
1.前台页面 <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> ...
- linux mint 五笔安装方法
终于可以使用五笔了,方法就是安装好ibus后要设置首选项,这样在首选项里设置就可以了.
- ExtJs 自定义Vtype验证
最近公司开发项目在用ExtJs,碰到验证的就大概的总结了一些常用的验证.自定义的验证主要有两种方式:一种是单字段的自定义验证,另一种是多字段间的验证.对于单字段的验证主要通过regex配置项指定自定义 ...
- bnuoj 29373 Key Logger(模拟双向队列)
http://www.bnuoj.com/bnuoj/problem_show.php?pid=29373 [题意]:模拟光标输入 [题解]:用双向列表模拟实现,这里用其他模拟会超时,注意内存的释放 ...
- spring mvc中的@PathVariable(转)
鸣谢:http://jackyrong.iteye.com/blog/2059307 ------------------------------------------------ spring m ...
- 让Flash背景透明兼容Firefox、IE 6和IE 7的代码
添加代码: <param name="wmode" value="transparent" > 到 <object>…</obje ...