`
frenchmay
  • 浏览: 233936 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

提高并发程序的可伸缩性的7个技巧

阅读更多

并发程序的可伸缩性主要是在保证程序安全同步,操作准确的前提下,保障程序在大量线程并发的情况下访问共享同步资源的效率.锁住某个共享的资源以获得独占式的访问这种做法会形成可伸缩性瓶颈――它使其他线程不能访问那个资源,即使有空闲的处理器可以调用那些线程也无济于事。为了取得可伸缩性,我们必须消除或者减少对独占式资源锁的依赖。

基于这种原理有如下几种解决方案,部分内容整理于ibm developerworks.

1 减小锁粒度

public class ShareHashMap {
	
	private static final Map<Integer,Map<String ,Object>> shareMap =
							 new HashMap<Integer,Map<String ,Object>>();
	
	public void addValue(Integer index,String key,Object value){
		checkIndex(index);
		shareMap.get(index).put(key, value);
	}
	
	public void addValue2(Integer index,String key,Object value){
		checkIndex(index);
		synchronized(shareMap.get(index)){
			shareMap.get(index).put(key, value);
		}	
	}
	
	public Object getValue(Integer index,String key){
		checkIndex(index);
		return shareMap.get(index).get(key);
	}
	
	public Object getValue2(Integer index,String key){
		checkIndex(index);
		synchronized(shareMap.get(index)){
			return shareMap.get(index).get(key);
		}	
	}
	
	private void checkIndex(Integer index){
		if(null==index){
			return;
		}
		synchronized(shareMap){
			if(null==shareMap.get(index)){
				shareMap.put(index, new HashMap<String ,Object>());
			}
		}
	}
}

  提高 HashMap 的并发性同时还提供线程安全性的一种方法是废除对整个表使用一个锁的方式,而采用对hash表的每个bucket都使用一个锁的方式(或者,更常见的是,使用一个锁池,每个锁负责保护几个bucket) 。这意味着多个线程可以同时地访问一个 Map 的不同部分,而不必争用单个的集合范围的锁。这种方法能够直接提高插入、检索以及移除操作的可伸缩性。不幸的是,这种并发性是以一定的代价换来的――这使得对整个 集合进行操作的一些方法(例如 size() isEmpty() )的实现更加困难,因为这些方法要求一次获得许多的锁,并且还存在返回不正确的结果的风险。然而,对于某些情况,例如实现cache,这样做是一个很好的折衷――因为检索和插入操作比较频繁,而 size() isEmpty() 操作则少得多。

 

实现1,为每个线程分配一个Integer类型的值,每个线程独享唯一一个键值对.这个原理和threadLocal的原理差不多,为每个线程分配独享的数据区域.下面是直接以当前线程为索引值,创建空间的一种实现.

 

public class ThreadHashMap {
	private static final Map<Thread,Map<String ,Object>> shareMap =
								 new HashMap<Thread,Map<String ,Object>>();
	
	public void addObject(String key,Object value){
		checkThread();
		shareMap.get(Thread.currentThread()).put(key, value);
	}
	
	public Object getObject(String key){
		checkThread();
		return shareMap.get(Thread.currentThread()).get(key);
	}
	
	private void checkThread(){
		Thread t = Thread.currentThread();
		synchronized(shareMap){
			if(null==shareMap.get(t)){
				shareMap.put(t, new HashMap<String ,Object>());
			}
		}
	}
}
 

 

实例2,运行多个线程读写同一个键值对,虽然也存在锁定,但是效率要比整个锁住整个hashmap要好的多.

 

2. 使用非堵塞的编程方式

使用ConcurrentHashMap,NIO等非堵塞资源提供方式.

如果拥有一个互斥锁,但是却堵塞导致其他线程等待甚至饥饿,是极其应该避免的.

具体内容可以见我在javaeye发布的文章线程,非堵塞队列和线程池相关小贴士

3. 根据访问操作的特征选择合适的数据结构.

如果读操作远高出写操作,建议使用类似org.apache.commons.collections的FastHashMap等快速集合,但是要注意

FastHashMap的写操作效率是很低的,写线程比较多的情况下很容易堵塞,另外FastHashMap是 以slow的方式运行的,要通过设置才能以fast模式运行,关于更多的FastHashMap内容可以看我在javaeye的另外一篇文章:关于FastHashMap

4. 合理使用线程池技术

线程池的使用一方面是为了节省线程创建的资源,或者例如网络连接,数据库连接只有有限的资源为了最大限度的利用这些资源,另一方面也是为了避免同时运行大量的线程,避免cpu在线程切换间浪费大量的资源.

5.CopyOnWrite

		Vector<String> v = new Vector<String>();
		for(int i=0;i<v.size();i++){
			v.get(i);
		}

 vector是线程安全的,但是通过索引遍历vector并不是安全的,虽然size()和get()方法每次调用都是线程安全的,但是遍历操作本身应该视为一个事务来处理,事务是具有原子性的.如果临时插入或者删除一个元素,导致index发生变更就有可能读取重复或者少读取元素.可以拷贝一个vecotr的副本,对副本进行遍历操作.

6. 锁崩溃

与其在获取锁和释放锁之间切换,不如一直占有锁,把当前的工作完成.获取您已占用的锁比获取无人占用的锁要快得多。

7.减小锁占用的时间

不要把和同步无关的代码,预处理代码和后期处理代码都放在同步块中.

分享到:
评论

相关推荐

    Java并发程序设计教程.pdf

    《Java并发程序设计教程》是一份详尽的指南,旨在帮助开发者掌握Java中的并发编程技巧。这份教程由温绍锦(昵称:温少)编写,涵盖了从基础到高级的各种并发概念和技术,对于希望深入理解并应用Java多线程机制的开发...

    Java并发程序设计教程

    Java并发程序设计教程主要围绕Java语言如何实现高效并发编程展开。在WEB开发领域,多线程...通过这些知识点的详细介绍和实践,开发者可以在Java平台上设计和实现高效率、可伸缩的并发程序,满足现代WEB应用的性能需求。

    Java并发编程实战华章专业开发者书库 (Tim Peierls 等 美Brian Goetz).pdf

    第三部分聚焦于性能优化,讲解了如何避免活跃性问题(如死锁和活锁)以及如何提高并发代码的性能和可伸缩性。此外,还介绍了测试并发代码正确性和性能的实用技巧,这对于在生产环境中确保程序的稳定性和效率至关重要...

    Java并发编程实战.pdf

    总体而言,Java并发编程实战》提供了一系列高级并发编程技巧和最佳实践,强调了并发程序设计中的效率和可靠性,同时指出了在并发程序设计中必须避免的陷阱和问题。这本经典书籍不仅对Java开发者有着重要的指导意义,...

    JAVA并发编程实践

    在Java编程中,并发是提高系统性能和可伸缩性的重要手段,也是现代软件开发中不可或缺的一部分。以下是该书可能涵盖的一些关键知识点: 1. **线程基础**:介绍Java中的线程创建方式,包括继承Thread类和实现...

    阿里专家级并发编程架构师教程[视频课程].txt打包整理.zip

    并发编程是计算机科学中的一个重要领域,它涉及到如何在单个或多个处理器上同时执行多个程序或程序的不同部分。在现代多核处理器系统中,利用并发可以极大地提高程序的执行效率和系统资源利用率。在Java、Python、...

    akka Essentials

    6. Akka在提升应用程序可伸缩性和性能方面的优势。 7. Java和Scala在Akka中的应用差异。 8. 在Akka框架下实现高性能并发应用程序的策略。 9. 解决性能和可伸缩性问题的方法。 10. 探索新兴开源技术的企业采纳情况。 ...

    提高ASP.NET应用程序性能方法浅析

    9. **利用负载均衡和分布式缓存**:在高并发场景下,通过负载均衡将请求分发到多个服务器,可以提高系统的可伸缩性。同时,使用分布式缓存(如Redis或Memcached)可以跨服务器共享数据,减轻单个服务器的压力。 10....

    Erlang程序设计(第2版)1

    书中详细阐述了如何编写这样的并发程序,并探讨了编写分布式应用程序的关键技巧,如如何设计能够容忍错误和故障恢复的系统。 分布式系统设计是Erlang的一大强项。Erlang允许开发者构建可以跨越多个节点运行的应用,...

    提高ASP.Net应用程序性能的十大方法

    在ASP.Net应用程序开发中,优化性能是至关重要的,因为这直接影响到用户的体验和系统的可伸缩性。以下是一些实用的技巧和建议,可以帮助提升ASP.Net应用的性能: 1. **减少数据库交互**: - **返回多个数据集**:...

    编写高性能WEB的十个技巧

    6. **返回多个结果集**:通过一次数据库请求获取多个结果集,减少往返数据库的次数,提高处理速度和系统伸缩性。 7. **使用存储过程**:存储过程可封装业务逻辑,减少数据传输量,但是否在存储过程中包含业务逻辑是...

    利用Interbase在C++Builder中开发C/S数据库应用程序

    它不仅支持多种开发工具和平台,而且提供了丰富的特性和功能,如分布式事务处理、高效的并发操作、可伸缩性、数据备份和恢复等。在C++Builder中开发基于InterBase的C/S架构应用程序时,开发者可以根据项目需求选择...

    java性能优化

    性能通常包括五个方面:运算性能、内存分配效率、启动时间、程序可伸缩性和用户感知性能。不同的应用场景可能对这些方面有不同的侧重,例如,服务器端应用可能更关注内存管理和可伸缩性,而桌面应用则可能更注重启动...

    java工程师必备

    性能包括运算性能、内存分配效率、启动时间、程序的可伸缩性以及用户感知的性能。不同的应用场景对这些方面有不同的侧重,例如服务器端应用可能更关注内存管理和处理高并发的能力,而桌面应用则可能更注重启动速度和...

    高清完整版 Mastering Concurrency Programming with Java

    2. **异步编程与响应式系统**:本书强调了现代应用程序如何利用异步编程模式(如响应式编程)来构建高性能且可伸缩的系统。通过使用如 Reactor 或 Akka 等库,开发人员可以更容易地编写出响应式的并发代码。 3. **...

    Java Concurrency in Practice

    本书将向你展示如何通过利用并发来提升程序的性能和可伸缩性,无论是在当下还是未来。此外,书中还涉及了并发编程中的一些根本性问题和复杂性,对于那些关心线程和性能的读者来说,这是一本必须阅读的书籍。 本书的...

    SQL入门常见问题总结与实用技巧介绍.docx

    分片则是将数据分布到多个物理位置,以提高可伸缩性和可用性。 #### 跨数据库操作 - **连接不同数据库**: - 使用Federated Engine(MySQL)、Linked Servers(SQL Server)等技术跨库查询。这些技术允许在一个...

    互联网高频Java后端面试题20道(适合1~3年)V1.0.32.docx

    使用RESTful API的好处在于简化系统架构,提高可伸缩性和可维护性,同时易于扩展和与其他系统集成。在实际应用中,开发人员通常会利用HTTP方法(GET、POST、PUT、DELETE等)来处理资源操作,从而实现灵活、可扩展的...

Global site tag (gtag.js) - Google Analytics