java并发编程 性能与伸缩_java并发编程(4)性能与可伸缩性

性能与可伸缩性

一、Amdahl定律

1.问题和资源的关系

在某些问题中,资源越多解决速度越快;而有些问题则相反:

a8549253acea72ae2d4df65b29f78cd3.png

注意:每个程序中必然有串行的部分,而合理的分析出串行和并行的部分对程序的影响极大;串行部分占比和多核执行效率之间是指数级别的关系

2.ConcurrentLinkedQueue

在多核环境中,这个线程安全的队列比通过synchronizedList生成的队列速度要快很多

可以说:concurrent中提供的类,比通过方法生成的线程安全类速度要快

二、线程开销

由于多线程有开销:所以使用多线程必须保证性能的提升>并发的开销

上下文切换的开销

内存同步的开销

三、减少锁的竞争

1.减少锁持有时间:缩小锁的范围

private final Map attributes = new HashMap();//整个方法上锁

public synchronized booleanuserLocationMatches(String name, String regexp) {

String key= "users." + name + ".location";

String location=attributes.get(key);if (location == null)return false;else

returnPattern.matches(regexp, location);

}public booleanuserLocationMatches(String name, String regexp) {

String key= "users." + name + ".location";

String location;//只针对可变状态上锁

synchronized (this) {

location=attributes.get(key);

}if (location == null)return false;else

returnPattern.matches(regexp, location);

}

2.降低锁的请求频率:锁分解、锁分段...

锁分解:将一个锁分解为多个锁如:无需在一个原子操作中更新多个状态变量,每个状态变量却用的是同一个类锁,就没必要,每个不相干的状态变量的使用自己的锁就行

public classServerStatusBeforeSplit {public final Setusers;public final Setqueries;publicServerStatusBeforeSplit() {

users= new HashSet();

queries= new HashSet();

}//每个方法使用 当前class实例锁,类似于synchronized(this),不管是否是操作同一共享状态

public synchronized voidaddUser(String u) {

users.add(u);

}public synchronized voidaddQuery(String q) {

queries.add(q);

}public synchronized voidremoveUser(String u) {

users.remove(u);

}public synchronized voidremoveQuery(String q) {

queries.remove(q);

}

}public classServerStatusAfterSplit {public final Setusers;public final Setqueries;//操作同一 状态的方法 使用相同的锁

publicServerStatusAfterSplit() {

users= new HashSet();

queries= new HashSet();

}public voidaddUser(String u) {synchronized(users) {

users.add(u);

}

}public voidaddQuery(String q) {synchronized(queries) {

queries.add(q);

}

}public voidremoveUser(String u) {synchronized(users) {

users.remove(u);

}

}public voidremoveQuery(String q) {synchronized(users) {

queries.remove(q);

}

}

}

锁分段:如将map桶分成不同的段,每个段都有一个锁,这样,在执行某些操作如get,就可以持有不同的锁从而提高并发效率,当然有些操作需要同时持有容器所有段的锁如clear等

//Map分段锁实现

public classStripedMap {//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 classNode {

Node next;

Object key;

Object value;

}public StripedMap(intnumBuckets) {

buckets= newNode[numBuckets];

locks= newObject[N_LOCKS];for (int i = 0; i < N_LOCKS; i++)

locks[i]= newObject();

}private final inthash(Object key) {return Math.abs(key.hashCode() %buckets.length);

}publicObject get(Object key) {int hash =hash(key);//获取当前 key对应的index区域的锁,只获取了一个锁

synchronized (locks[hash %N_LOCKS]) {for (Node m = buckets[hash]; m != null; m =m.next)if(m.key.equals(key))returnm.value;

}return null;

}public voidclear() {for (int i = 0; i < buckets.length; i++) {//获取 每个i对应的锁,就是获取了整个容器所有的分段锁

synchronized (locks[i %N_LOCKS]) {

buckets[i]= null;

}

}

}

}

3.避免热点域

热点资源的锁竞争激烈,导致的性能问题

4.替代独占锁

如:读-写锁:读读可并行,来防止独占;使用原子状态量;使用并发容器;使用不可变对象等

5.减少上下文切换

任务在阻塞于非阻塞的状态中切换,就类似于一次上下文切换

如:日志,日志的打印和IO操作会导致大量的阻塞和释放,导致性能问题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值