1. 什么是单例模式
- 该类只能有一个实例;
- 该类能够自动实例化;
- 对整个系统可见,即必须向整个系统提供这个实例。
2. 实现
a. 饿汉式
public class Singleton {
private static Singleton instance=new Singleton();
private Singleton(){
}
public static Singleton getInstance(){
return instance;
}
}
实现简单,基于classloder机制避免了多线程的同步问题,不过,instance在类装载时就实例化,没有进行延迟加载。
b. 懒汉
public class Singleton{
private static Singleton instance;
private Singleton(){
}
public static Singleton getInstance(){
if(instance==null){
instance=new Singleton();
}
return instance;
}
}
lazy loading很明显,但是在多线程时不能正常工作
c. 懒汉 + 同步方法
public class Singleton{
private static Singleton instance;
private Singleton(){
}
public static synchronized Singleton getInstance(){
if(instance==null){
instance=new Singleton();
}
return instance;
}
}
解决了多线程同步,但同步范围太大,在实例化instacne后,获取实例仍然是同步的,效率低
d. 懒汉 + 同步块
public class Singleton{
private static Singleton instance;
private Singleton(){
}
public static Singleton getInstance(){
if(instance==null){
synchronized(Singleton.class){
instance=new Singleton();
}
}
return instance;
}
}
缩小了同步范围,但存在创建多个实例的可能,比如线程A和B同时执行到了synchronized(Singleton.class),线程B获得锁执行完之后,线程A获得锁,但是此时没有检查singleton是否为空就直接执行了,所以还会出现两个singleton实例的情况
e. 懒汉 + 双重检查(DCL)
public class Singleton{
private static volatile Singleton instance;
private Singleton(){
}
public static Singleton getInstance(){
if(instance==null){
synchronized(Singleton.class){
if(instance==null){
instance=new Singleton();
}
}
}
return instance;
}
}
instance=new Singleton()并不是原子语句,实际包括了下面三大步骤:
1. 为对象分配内存
2. 初始化实例对象
3. 把引用instance指向分配的内存空间
处理器会进行指令重排序优化,当重排后执行顺序为1,3,2时,若线程1执行到3,此时线程2判断instance!=null,直接返回instance引用,但实际上实例对象还没有初始化完毕,使用它可能会造成程序崩溃。
e. 懒汉 + 双重检查 + volatile
public class Singleton{
private static volatile Singleton instance;
private Singleton(){
}
public static Singleton getInstance(){
if(instance==null){
synchronized(Singleton.class){
if(instance==null){
instance=new Singleton();
}
}
}
return instance;
}
}
使用volatile提供内存屏障,限制处理器进行指令优化重排,保证了instance赋值操作是最后一步完成,不会再出现instance在对象没有初始化时就不为null的情况
f. 懒汉+静态内部类
public class Singleton{
private Singleton(){
}
public static Singleton getInstance(){
return InstanceHolder.instance;
}
static class InstanceHolder{
private static Singleton instance = new Singleton();
}
}
静态内部类在Singleton类被装载时并不会立即实例化,而是在调用getInstance方法,才会装载SingletonInstance类,从而完成Singleton的实例化。
类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。
g. 枚举
public enum EasySingleton {
INSTANCE;
}
借助JDK1.5中添加的枚举来实现单例模式。不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。