通常单例模式在Java语言中,有两种构建方式:
- 懒汉方式:指全局的单例实例在第一次被使用时构建。延迟初始化。
- 饿汉方式:指全局的单例实例在类装载时构建。 急切初始化。
1,饿汉式单例类
/**
* 饿汉模式,是线程安全的,但是性能不好
*
@author
Administrator
*/
public
class
StarvingSingleton {
// 私有化构造函数
private
StarvingSingleton(){}
// 类加载时 即初始化
private
static
final
StarvingSingleton
INSTANCE
=
new
StarvingSingleton();
public
static
StarvingSingleton getInstance(){
return
INSTANCE
;
}
}
2,懒汉式单例类
/**
* 懒汉模式,在使用时在初始化,有多线程问题,需要做同步处理
*
@author
Administrator
*/
public
class
LazySingletion {
//属性instatnce是被volatile修饰的,因为volatile具有synchronized的可见性特点,
//也就是说线程能够自动发现volatile变量的最新值。
//这样,如果instatnce 实例化成功,其他线程便能立即发现。
private
static
volatile
LazySingletion
INSTANCE
=
null
;
private
LazySingletion(){}
//做双重检查
public
static
LazySingletion getInstance(){
if
(
INSTANCE
==
null
){
synchronized
(LazySingletion.
class
) {
if
(
INSTANCE
==
null
){
INSTANCE
=
new
LazySingletion(){};
}
}
}
return
INSTANCE
;
}
}
volatile
volatile, 用更低的代价替代同步
为什么使用volatile比同步代价更低?
同步的代价, 主要由其覆盖范围决定, 如果可以降低同步的覆盖范围, 则可以大幅提升程序性能.而volatile的覆盖范围仅仅变量级别的. 因此它的同步代价很低.
volatile原理是什么?
volatile的语义, 其实是告诉处理器, 不要将我放入工作内存, 请直接在主存操作我.(工作内存详见java内存模型).因此, 当多核或多线程在访问该变量时, 都将直接操作主存, 这从本质上, 做到了变量共享.
volatile的有什么优势?
1, 更大的程序吞吐量
2, 更少的代码实现多线程
3, 程序的伸缩性较好
4, 比较好理解, 无需太高的学习成本
volatile有什么劣势?
1, 容易出问题
2, 比较难设计
这是由于Java平台的内存模式容许out-of-order writes引起的,假定有两个线程,Thread 1和Thread 2,它们执行以下步骤:
1. Thread 1发现instatnce没有被实例化,它获得锁并去实例化此对象,JVM容许在没有完全实例化完成时,instance变量就指向此实例,因为这些步骤可以是out-of-order writes的,此时instance==null为false,之前的版本即使用volatile关键字修饰也无效。
2. 在初始化完成之前,Thread 2进入此方法,发现instance已经不为null了,Thread 2便认为该实例初始化完成了,使用这个未完全初始化的实例对象,则很可能引起系统的崩溃。
现在流行用枚举类型做
public
enum
EnumSingletion {
INSTANCE
;
}
默认枚举实例的创建是线程安全的,但是在枚举中的其他任何方法由程序员自己负责。
传统单例存在的另外一个问题是一旦你实现了序列化接口,那么它们不再保持单例了,因为readObject()方法一直返回一个新的对象就像java的构造方法一样,你可以通过使用readResolve()方法来避免此事发生,看下面的例子:
private Object
readResolve(){
return INSTANCE;
}
但是枚举单例,JVM对序列化有保证
参考:
http://www.importnew.com/6461.html
http://www.importnew.com/6662.html