单例模式浅析

 

浅析单例模式

单例模式概述:

单例模式,我觉得是多种设计模式中最简单、也是应用最多的设计模式。顾名思义,单例模式指的是该类的对象实例为全局唯一变量,也可以说,一个类只有一个实例对象。
具体实现步骤:
1..构造方法私有化 。
2.在类内部创建私有静态对象实例。
3.提供公有获取唯一实例的方法。。

常见的单例模式写法:

饿汉式:

public class singleton {
    //构造方法私有化
    private singleton(){
    }
    //创建私有实例对象
    private   static  final singleton instance=new singleton();
    //公有方法获取唯一实例
    public static singleton  getInstance(){
        return  instance;
    }
}

饿汉式,在类加载的时候就会创建唯一的实例对象供外部使用,优点是线程安全。缺点是类加载就创建实例,可能造成内存的浪费。

懒汉式(延迟加载方式):

public class singleton {
     private singleton(){
     }
     //实例化对象先为空
     private static  singleton  instance=null;
     //先判断对象是否对象是否为空,如果为空,才开始创建实例化对象。
     public static singleton getInstance(){
         if(instance==null){
             instance=new singleton();
         }
         return instance;
     }

}

懒汉式,采用延迟加载的方式,即类加载的时候并不会创建实例化对象,只有真正用到这个对象,调用getInstance()方法的时候才会去实例化这个对象,所以叫做懒汉式。
懒汉式方法的优点是,并不会浪费系统内存。缺点是并不是线程安全的。
那为了实现线程安全,我们自然会想到使用synchronized同步锁。那到底行不行呢?
懒汉式使用synchronized同步锁:

public class singleton {
    //构造方法私有化
    private singleton(){
    }
    //实例化对象先为空
    private static  singleton  instance=null;
    //为该方法加锁,保证线程安全。
    public static  synchronized singleton getInstance(){
        if(instance==null){
            instance=new singleton();
        }
        return instance;
    }

}

这样做导致的问题是在多线程情况下会导致性能比较低下。那我们可以考虑一下,是不是可以把锁的范围缩小呢?

所以代码可以这样写:

public class singleton {
    //构造方法私有化
    private singleton(){
    }
    //实例化对象先为空
    private static  singleton  instance=null;
    //为该方法加锁,保证线程安全。
    public static   singleton getInstance(){
        if(instance==null){
            synchronized(singleton.class) {
                instance = new singleton();
            }
        }
        return instance;
    }
}

表面看上去没什么问题,其实仔细考虑我们就会发现可能会出现两个实例化对象的问题。
考虑这样的情况。线程A和线程B同时进入到getInstance()方法里。都判断instance==null,所以都进入下面的if()代码块了。
假设线程A得到cpu使用权,进入到同步代码块,创建了对象并返回对象。当线程A完成以后,此时线程B继续进入同步代码块,就会创建另一个对象并返回。
所以这样的话就会返回不止一个对象实例啦。
我们可以看一下测试代码的结果:
enter description here

所以为了解决这种问题,我们可以在锁内再判断一次是否对象是否为空。这就是DCL(双重检测机制)懒汉式。

其实这样还不完全稳,会有重排序的问题,这里就不细说,感兴趣的自己了解。要解决重排序也很简单,使用volatile关键字,具有内存屏障的功能。下面是完整的代码:

双重检测机制(DCL)懒汉式:

public class singleton {
    //构造方法私有化
    private  singleton(){
    }
    //实例化对象先为空
    private static volatile singleton  instance=null;
    //为该方法加锁,保证线程安全。
    public static   singleton getInstance(){
        if(instance==null){
            //锁范围缩小
            synchronized(singleton.class) {
                //再次判断是否为空
                if(instance==null) {
                    instance = new singleton();
                }
            }
        }
        return instance;
    }

}

另外一种推荐使用的方法就是静态内部类的方法:
静态内部类:

public class singleton {
    //构造方法私有化
    private singleton(){
    }
    //私有静态内部类
    private static class LazyHolder{
        //创建单例对象
        private static final singleton instance=new singleton();
    }
    //获取单例对象
    public static final singleton getInstance(){
        return LazyHolder.instance;
    }

}

该方法的原理是多线程情况下,不管哪个线程第一次调用getInstance方法都会LazyHolder被加载和初始化,而当初始化静态数据时,java是线程安全的。

最后一种实现方式比较简单,使用枚举:
枚举方法:

public enum Singleton {  
    instance;  
    public void whateverMethod() {  
    }  
}

这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化。
由于 JDK1.5 之后才加入 enum 特性,用这种方式写不免让人感觉生疏,在实际工作中,也很少用。

猜你喜欢

转载自www.cnblogs.com/fankailei/p/10567605.html