多线程并发的最佳实践
1. 使用本地变量:应该尽量使用本地变量,而不是创建一个实例的变量
2.使用不可变类:比如String,Integer等,一旦创建就不会改变,不可变类可以降低代码中的同步数量
3.最小化锁的作用域范围:上锁和解锁之间的代码,越少越好
4.使用线程池的Executor,而不是直接new Thread执行:创建一个线程的代价是昂贵的,如果想要的到一个可伸缩的Java应用,那么需要使用线程池管理线程
5.宁可使用同步也不要使用线程的wait和notify:从Java1.5以后增加了许多同步工具,比如countDownLatch,semphore等等,应当优先使用这些同步工具,通过使用BlockingQueue来实现生产线程的队列化,比使用wait和notfy要简单,且易于开发。也可以使用countDownLatch来实现多个线程的等待
6.使用BlockingQueue实现生产-消费模式:大部分并发问题都可以使用生产-消费问题实现,而BlockingQueue是最好的阻塞队列实现类,它既可以处理单个生产单个消费,也可以处理多个生产多个消费。
7.使用并发集合而不是加了并发锁的同步集合:Java提供了ConcurrentHashMap,CopyOnWriteArrayList,CopyOnWriteArraySet等,建议使用这些,而不是加了同步锁的synchronized**这样的集合
8.使用Semaphore创建有界的访问:为了建立可靠稳定的系统,对于数据库,文件系统,socket等资源,必须要做有界的访问,Semaphore是一个可以限制资源开销的选择,使用它可以以最低的代价阻塞线程,也可以通过Semaphore来控制指定资源的访问线程数。
9.宁可使用同步代码块也不要使用同步方法:这里主要是针对synchronized关键字,使用synchronized代码块只会锁定一个对象,而不是将整个方法锁定,如果更改共同的变量或类的字段首先应该选择的是原子性变量,然后使用volitale。如果想要控制锁,可以考虑使用ReenTrantLock
10.避免使用静态变量:静态变量在并发执行环境下会造成很多问题,如果必须要使用静态变量,那么优先添加final关键字,如果是用来保存connection集合的话,那么优先使用只读集合,否则一定要做特别多的同步处理,以及并发处理。
Spring与线程安全
Spring作为一个IOC容器,帮助我们管理了许许多多的bean,但是spring并没有保证这些bean的线程安全。Spring为每个bean内置了域scope为别有:
Spring IOC内的scope类型:
singleton : 默认,一个Spring IoC容器中只能有一个bean实例,容器启动时初始化,单例模式,生命周期与Spring容器一致
prototype :在一个Spring IoC容器中可以有多个bean实例,每次被调用getter时初始化
request:bean实例的生命周期只在一次HTTP请求中,即每次HTTP请求都创建一个新的bean实例,只能在WebApplicationContext上下文中配置,如XmlWebApplicationContext
session: bean实例的生命周期在HTTP session中,只能在WebApplicationContext上下文中配置,如XmlWebApplicationContext
global session:bean实例的生命周期在全局的HTTP session中(典型地,跨portlet),只能在WebApplicationContext上下文中配置,如XmlWebApplicationContext
application: bean实例的生命周期在ServletContext中,只能在WebApplicationContext上下文中配置,如XmlWebApplicationContext
而我们交由Spring管理的对象大多是无状态对象,其实都是一些无状态对象,这种不会因为多线程被破坏的对象才适合Spring的默认socpe。无状态对象其实说白了就是自身没有状态的对象,它不会因为多线程的改变而改变自身的状态,它包括了我们经常使用的DTO,VO这写只作为数据的实体模型对象,还有我们使用的Service,DAO和Controller等等,这些对象并没有自己的状态,他们只是用来执行某些操作的,例如我们DAO提供的每个函数,都只是对数据库的CRUD操作,而且每个数据库的Connection都是作为函数的局部变量用完即关,交还连接池。
说了这么多,大概意思就是虽然Spring 的Bean无法保证线程安全,但是我门交由Spring管理的对象默认即无状态对象,所以非特殊情况还是能够保证线程安全