2 创建型设计模式之单例模式

1. 单例模式有哪些特点:

1.1 不允许 new , 保证唯一性

1.2 对外提供获取对象唯一实例的接口方法

 

2.实现方式

2.1 简单 单例模式 , 线程安全(饿汉式)

public class Test{  
    // 定义一个私有的构造方法,阻止外部 new 这个对象实现实例化
    private Test() {   }  
    // 将自身的实例对象设置为一个属性,并加上Static和final修饰符
    private static final Test test= new Test();  
    // 静态方法返回该类的实例
    public static Test getTest() {  
        return test;  
    }  
}

2.2 如2.1所示的单例模式,如果有100000这样的单例在部署发布后就有100000个对象已经被实例化了,这种情况占了服务器资源,优化后如下所示:(饱汉式,线程不安全)

public class Test{
    // 定义私有构造方法
    private Test() {     }   
    // 不初始化,注意这里没有使用final关键字
    private static Test test;   
    // 调用时再初始化Test,但是多线程访问时,可能造成重复初始化问题
    public static Test getTest() {   
        if (test== null) test= new Test();   
        return test;   
    }   
}

2.3 如2.2所示例子会出现线程造成重复初始化问题,优化如下:(饱汉式,线程安全的简单实现)

public class Test{
    // 防止通过 new 去实例化
    private Test() {   }   
    // 不初始化,注意这里没有使用final关键字
    private static Test test;   
    // 使用synchronized 避免多线程访问时,可能造成重的复初始化问题
    public static synchronized  Test getTest() {   
        if (test == null)   
            test = new Test();   
        return test;   
    }   
}

2.4 如2.3所示例子已经达到了单例模式的标准,但出于性能考虑,还需要继续优化,代码如下:(饱汉式,线程安全 并且效率高  单例模式最优方案)

public class Test { 
    // 定义一个私有构造方法
    private Test () { }   
    //使用volatile保证了多线程访问时test变量的可见性,避免了test初始化时其他变量属性还没赋值完时,被另外线程调用
    private static volatile Test test;  
    //定义一个共有的静态方法,返回该类型实例
    public static Test getTest () { 
        // 不使用同步代码块,test不等于null时,直接返回对象,提高运行效率
        if (test== null) {
            // 对象未初始化时,使用同步代码块,保证多线程访问时对象在第一次创建后,不再重复被创建
            synchronized (Test.class) {
                //未初始化,则初始instance变量
                if (test== null) {
                    test= new Test ();   
                }   
            }   
        }   
        return test;   
    }   
}

2.5 静态内部类方式

public class Test{ 
     
    private static class TestHolder {  
     	private static final Test TEST = new Test ();  
    }
  
    private Test(){}

    public static final Test getTest() {  
        return TestHolder.TEST ;  
    }  
}

// 这种方式同样利用了classloder的机制来保证初始化 test 时只有一个线程
// 它跟第三种和第四种方式不同的是:
//    第三种和第四种方式是只要Test类被装载了,那么test就会被实例化(没有达到lazy loading效果)
//    而这种方式是Test类被装载了,test不一定被初始化。

目前最为安全的实现单例的方法是通过内部静态enum的方法来实现,因为JVM会保证enum不能被反射并且构造器方法只执行一次。如下:

2.6 使用枚举的单例模式

public class EnumSingleton{
    private EnumSingleton(){}
    public static EnumSingleton getInstance(){
        return Singleton.INSTANCE.getInstance();
    }
    private static enum Singleton{
        INSTANCE;
        private EnumSingleton singleton;
        //JVM会保证此方法绝对只调用一次
        private Singleton(){
            singleton = new EnumSingleton();
        }
        public EnumSingleton getInstance(){
            return singleton;
        }
    }
}

2.7 使用枚举,static处调用,初始化一次

public class StaticInitTest {
    public static List<Integer> dataList = null;
    static{
        dataList = Singleton.INSTANCE.init();
    }
    private static enum Singleton {
        INSTANCE;
        private List<Integer> list;
        private Singleton(){
            fillData();
        }
        // 初始化数据
        private void fillData(){
            list = new ArrayList<Integer>(5);
            for(int i =1; i<6; i++){
                list.add(i);
            }
        }
        // 初始化入口
        public List<Integer> init(){
            return list;
        }
    }
}

 

猜你喜欢

转载自blog.csdn.net/xiaofather/article/details/85113693