单例模式

单例模式

起男 1,063 2021-03-25

单例模式

所谓的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态的)

饿汉式(静态常量)

  1. 构造器私有化(防止new)
  2. 类的内部创建对象
  3. 向外暴露一个静态的公共方法
class Singleton{
    //1.构造器私有化
    private Singleton(){}
    //2.类内部创建对象实例
    private final static Singleton singleton = new Singleton();
    //3.提供一个公共的静态方法,返回实例对象
    public static Singleton getSingleton(){
        return singleton;
    }
}

优点 :简单,因为在类装载时就完成了实例化。避免了线程同步的问题

缺点:在类装载的时候完成实例化,没有达到懒加载的效果。如果自始至终没有使用这个实例,则会造成浪费

饿汉式(静态代码块)

将实例的创建放到静态代码块中

class Singleton{
    //1.构造器私有化
    private Singleton(){}
    //2.类内部创建对象实例
    private final static Singleton singleton;
    
    //2.1在静态代码块中创建对象
    static{
        singleton = new Singleton();
    }
    
    //3.提供一个公共的静态方法,返回实例对象
    public static Singleton getSingleton(){
        return singleton;
    }
}

懒汉式(线程不安全)

class Singletion{
	private Singletion(){}
    
    private static Singletion singletion;
    
    //提供一个静态共有方法,当使用该方法时,才去创建
    public static Singletion getSingletion(){
        if(singletion == null){
            singletion = new Singletion();
        }
        return singletion;
    }
}

优点:需要时才创建实例,节省资源

缺点:线程不安全,只能单线程中使用。当一个线程进入if判断语句时,还没来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例

懒汉式(线程安全,同步方法)

class Singletion{
	private Singletion(){}
    
    private static Singletion singletion;
    
    //在方法上添加synchronized 来保证线程安全
    public static synchronized Singletion getSingletion(){
        if(singletion == null){
            singletion = new Singletion();
        }
        return singletion;
    }
}

优点:解决了线程安全问题

缺点:效率太低,每个线程在获取实例的时候,执行方法都要进行同步。而其实这个方法只执行一次实例化就足够了,后面的想获取实例之间return就行了

懒汉式(线程安全,同步代码块)

class Singletion{
	private Singletion(){}
    
    private static Singletion singletion;
    
    //用同步代码块代替同步方法 但是无效
    public static Singletion getSingletion(){
        if(singletion == null){
            synchronized(Singletion.class){
             	singletion = new Singletion();   
            }
        }
        return singletion;
    }
}

这种方式本意是对同步方法方式的改进,因为同步方法效率太低,改为同步产生实例化的代码块

但是这种方式并不能起到线程同步的作用。跟线程不安全的方式遇到情况一样,假如一个线程进入了if,但还未创建实例,另一个线程也进入了if,则创建了多个实例

双重检查(DoubleCheck)

class Singletion{
	private Singletion(){}
    //用volatile进行修饰,让修改值立即更新到主存
    private static volatile Singletion singletion;
    
    //解决线程安全问题、效率和懒加载问题
    public static Singletion getSingletion(){
        if(singletion == null){
            //创建实例后就不会在执行同步代码
            synchronized(Singletion.class){
                if(singletion == null){
                 	singletion = new Singletion();      
                }
            }
        }
        return singletion;
    }
}

进行了两次if检查,这样可以保证线程安全

实例化代码只执行一次,后续在访问时,直接return实例,避免了同步代码

静态内部类

静态内部类特点:

  1. 外部类装载时,静态内部类不会立即装载(保证懒加载)
  2. 当调用静态内部内里的静态变量时,静态内部类会被装载。jvm类装载时线程是安全的(保证线程安全)
class Singletion{
    //构造器私有
	private Singletion(){}
    
    //静态内部类,类中有一个静态属性
    private static class SingletionInstance{
        private static final Singletion singletion = new Singletion();
    }
    
    //之间返回内部类的静态属性
    public static Singletion getSingletion(){
        return SingletionInstance.singletion;
    }
}

这种方式采用了类装载的机制来保证初始化实例只有一个线程

静态内部类方式在Singleton类被装载时并不会立刻实例化,二十在需要实例化时,调用实例化方法,才会装载内部类,完成实例化

类的静态属性只会在第一次加载类的时候初始化,所以在这里,jvm帮我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的

枚举

使用枚举可以实现单例

enum Singleton{
    singletion;
}

不仅能够避免多线程的问题,而且还能防止反序列化重新创建新的对象