/**
* 单例模式(单子模式):确保某个类只有一个实例,
* 而且自行实例化并向整个系统提供这个实例的单例模式;
* 适用在有真正“单一实例”需求时;
*
一个类能否做成单例取决于 在应用中如果有两个或者两个以上的实例是否会引起错误(包括程序错误和逻辑错误)
好处:节约内存空间,减少无谓的GC消耗
* 下面介绍5种写法:
* 第一种方法:多线程中不能正常工作;
* 1.声明一个私有的静态的当前类的对象;
* 2.让构造器私有;
* 3.声明一个公共的静态返回自己的方法;
* 4.在该方法内部将当前类的对象自行实例化;
* 第二种方法:适用多线程,但效率低;
* 同第一种方法,唯一的不同就是在声明方法时加一个“synchronized”表示同步的关键字;
* 第三种方法:是第二种方法的升级版,又称双重检查锁定版;
* 1.声明一个私有的静态的当前类的对象,但要加上volatail类型修饰符;
* 2.让构造器私有;
* 3.声明一个公共的静态返回自己的方法;
* 4.在该方法内部将当前类的对象自行实例化,但用synchronized关键字进行锁定当前类;
(推荐)第四种方法:应用内部类实现,适用于多线程,采用的是classloader机制保证实例化时只有一个线程,是比较安全的单子模式,但编写有点麻烦;
* 1.编写一个私有的静态的内部类,在类中实现“单一实例”(是私有的静态的常量);
* 2.让构造器私有;
* 3.声明一个公共的静态返回自己的方法;
* 4.在该方法的内部返回内部类中的对象常量;
* 第五种方法:是第四种方法类似;
* 1.直接初始化单例的实例;
* 2.让构造器私有;
* 3.在该方法内部返回实例;
*/
第一种方法:
/** * 在不考虑并发访问的情况下标准的单例模式的构造方式 */ public class Singleton1 { //这里必须用staic,因确保某个类只有一个实例,也是为了在getInstance()这个静态方法中使用它; private static Singleton1 s; //首先将构造函数私有;使在在类的外部不能被实例化; private Singleton1() { }
//这里必须用staic,因为无法通过动态的实例化对象调用,只能是静态的; public static Singleton1 getInstance() { if(s == null) { s = new Singleton1(); } return s; } }
第二种方法:
/** * 考虑并发的情况了,我们最容易想到的方式应该是下面这样的方式,直接将整个方法同步 (synchronized) */ public class Singleton2 { private static Singleton2 s; private Singleton2() { } public static synchronized Singleton2 getInstance() { if(s == null) { s = new Singleton2(); } return s; } }
第三种方法:
/** * 很多教科书中标准的单例模式版本,也称为双重加锁,但在其基础上又使用valatile类型修饰符。故暂且称为:双重锁定检查版的单例 * * 加volatile关键字的好处: * 如果深入到JVM中去探索不加volatile的双重加锁的代码,它是有可能(注意,只是有可能)发生问题的。 因为虚拟机在执行创建实例的这一步操作的时候,其实是分了好几步去进行的,也就是说创建一个新的对象并非是原子性操作。 如果按照正常顺序执行,那就没有问题;怕就怕JVM不按常理出牌,进行指令的调优,那就可能会出问题了; 因此在某些特定的情况下是会造成莫名的错误; */ public class Singleton3 { /* * volatile是一个类型修饰符;它是被设计用来修饰被不同线程访问和修改的变量。 * 如果不加volatile,基本上会导致这样的结果:要么无法编写多线程,要么编译器失去大量优化的机会; * 它的作用:作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值; * 简单来说就是防止编译器对代码进行优化; * 一般适用于多线程并行开发; */ private static volatile Singleton3 s; //第一重锁定,用volatile关键字; private Singleton3() { } public static Singleton3 getInstance() { if(s == null) { synchronized (Singleton3.class) { //第二种锁定,用synchronized关键字; if(s == null) { s = new Singleton3(); } } } return s; } }
第四种方法:
/** * 最为推荐的单例模式写法:使用静态的内部类作为单例 */ public class Singleton4 { //用一个私有的静态的内部类来实现单例模式; private static class SingletonHolder { //是私有的静态常量; private static final Singleton4 INSTANCE = new Singleton4(); } private Singleton4() { } public static Singleton4 getInstance() { return SingletonHolder.INSTANCE; } }
第五种方法:
package com.singleton; /** * 俗称“饿汉式加载”的单例模式(其实跟Singleton3内部类的方式类似) * 注: * 这种方式最主要的缺点就是一旦我访问了Singleton5类的任何其他的静态域, * 就会造成实例的初始化,而事实是可能我们从始至终就没有使用这个实例,造成内存的浪费。 * 不过在有些时候,直接初始化单例的实例也无伤大雅,对项目几乎没什么影响 */ public class Singleton5 { private static Singleton5 singleton = new Singleton5(); private Singleton5(){} public static Singleton5 getInstance(){ return singleton; } }