1.单例模式有什么用处?

有一些对象只能使用一个,例如:数据库连接、线程池(threadpool)、缓存(cache)、对话框、处理偏好(preferences)设置和这侧表(registry)的对象、日志对象、充当打印机、显卡等设备的驱动程序的对象,即用于管理共享的资源。这种对象只能有一个实例,制造多个会导致问题。


2.最经典的单例模式?

        /**
     * 1.利用一个私有静态变量来记录Singleton类的唯一实例;
     * 2.构造器声明为私有,只有Singleton类本身内部才可调用;
     * 3.声明共有静态的方法getInstance()来实例化对象,并返回这个对象的实例;
     * 4.说明:如果我们不需要这个实例,就不会调用getInstance()方法,那么这个实例就不会产生,
     *        称为“延迟实例化(lazy instantiaze)”。这种做法对资源敏感的对象特别重要。
     */
    public class Singleton {
    
        private static Singleton sInstance;
        
        private Singleton(){}
        
        public static Singleton getInstance(){
            if(sInstance == null){
                sInstance = new Singleton();
            }
            return sInstance;
        }
    }

3.单例模式定义--确保一个类只有一个实例,并提供一个全局访问点

(1)全局访问点即向外提供的获取唯一实例的唯一入口,是共有静态的,访问它对比访问一般的全局变量的优点在于:单例中的全局访问点可以做到“延迟实例化”。

(2)此时的单例可能遇到的麻烦--多线程

         正如下图中情况所示,当有多个线程同时访问该访问点时,以两个线程为例,就会出现可能实例化两个对象,这对于使用单例的地方可能引发灾难呀...

Java单例模式以及线程安全性的保证_第1张图片


4.怎样改善多线程情况下的单例?

(1)不推荐的方法--synchronized

        同步getInstance()方法既简单又有效,但是可能造成程序执行效率下降100倍,如果getInstance()被调用的频繁,只能重新考虑其他方法了;

(2)什么都不做

        没错,什么都不做。如果应用程序可以接受getInstance()造成的额外负担,那就完全可以忘记这件事;

(3)使用“急切实例化(eagerly)”而非“延迟实例化”的做法

        a.如果应用程序总是需要创建使用单例实例;

        b.如果应用程序在创建运行时方面的负担不太重;

        此时可选择“急切实例化”:

        public class EagerlySingleton {        
            /**在静态初始化器中创建单例实例,保证了线程安全**/
            private static EagerlySingleton eSingletonInstance = new EagerlySingleton();
            
            private EagerlySingleton(){};
            
            public static EagerlySingleton getInstance(){
                return eSingletonInstance;
            }
        }

        这个做法,依赖JVM在加载这个类时就创建这个唯一的单例实例,保证了任何线程在访问这个静态变量之前,该实例已经创建。

(4)用“双重检查加锁(double checked locking)”,在getInstance()中减少使用同步

    /**    
     * 1.volatile确保实例化时,多个线程能正确的处理sInstance;
     * 2.判断sInstance == null后在使用synchronized,且synchronized后在判断一次sInstance == null
     * 3.双重检查加锁不适用于1.4以及更早的版本的Java。
     */
    public class DoubleCheckSingleton {
    
        private volatile static DoubleCheckSingleton sInstance;
        
        private DoubleCheckSingleton(){}
        
        public static DoubleCheckSingleton getInstance(){
            if(sInstance == null){
                synchronized (DoubleCheckSingleton.class) {
                    if(sInstance == null){
                        sInstance = new DoubleCheckSingleton();
                    }
                }
            }
            return sInstance;
        }
    }