Java中的单例模式

Java基础

浏览数:259

2019-8-19

 一、饿汉式

public class HungerySingleton {
 
 
    //ClassLoader 类加载时立即实例化对象,仅实例化一次,线程安全的
    private static HungerySingleton hungerySingleton = new HungerySingleton();
 
 
    public static HungerySingleton getInstance(){
        return hungerySingleton;
    }
 
    //利用线程输出(后面就不写了哈)
    public static void main(String[]args){
        for(int i=0;i<20;i++){
            Thread thread =new Thread() {
                @Override
                public void run() {
                    HungerySingleton hungerySingleton = HungerySingleton.getInstance();
                    System.out.println(hungerySingleton);
                }
            };
            thread.start();
        }
    }
}

优点:仅实例化一次,线程是安全的。获取实例的速度快

缺点:类加载时立即实例化对象,可能实例化的对象不被使用,造成内存的浪费。    

二、懒汉式

public class HoonSingleton {
    //不能保证实例对象的唯一性
    private static HoonSingleton hoonSingleton = null;
 
 
    public static HoonSingleton getInstance(){
        if(hoonSingleton==null){
            hoonSingleton = new HoonSingleton();
        }
        return hoonSingleton;
    }
}

优点:获取实例时才进行实例的初始化,节省系统资源

缺点:1、如果获取实例时,初始化的工作量较多,加载速度会变慢,影响系统系能           2、每次获取实例都要进行非空检查,系统开销大           3、非线程安全。注意红色代码标记,当多个线程同时getInstance()时,可能hoonSingleton实例化未完成,hoonSingleton==null判断均为true,造成对象重复实例化。    

三、双重检查锁 DCL(double-checked locking)+ volatile

public class HoonSingleton {
 
    private static volatile HoonSingleton hoonSingleton = null;
 
    // 使用sync同步HoonSingleton.class 两次判断hoonSingleton是否为null 避免并发导致hoonSingleton被重新实例化
    // 并没有对整个方法使用sync,锁的粒度变小了,实现了实例对象的唯一性
    public static HoonSingleton getInstance(){
        if(hoonSingleton==null){
            synchronized (HoonSingleton.class) {
                if(hoonSingleton==null) {
                    hoonSingleton = new DCL();
                }
            }
        }
        return hoonSingleton;
    }

优点:1、线程安全。注意加粗标记,进行双重检查,保证只在实例未初始化前进行同步,效率高。

          2、对hoonSingleton使用volatile修饰符,避免实例化过程中产生的重排序。避免NPE抛出。 缺点:实例非空判断,耗费一定资源    

四、Holder方式 广泛使用的一种单例模式

//声明类的时候、成员变量中不声明实例变量,而是放到内部静态类中
public class HolderDemo {
 
    private static class Holder{
        private static HolderDemo instance = new HolderDemo();
    }
 
    public static HolderDemo getInstance(){
        return Holder.instance;
    }
}

优点:1、内部类只有在外部类被调用才加载,从而实现了延迟加载

          2、线程安全。且不用加锁。      

五、使用枚举的单例模式,本质上和饿汉模式没有任何区别,只是采用Enum实现的更巧妙了

public enum EnumSingleton {
    //枚举类型,在加载的时候实例化。
    INSTANCE;
    public static EnumSingleton getInstance(){
        return INSTANCE;
    }
}

优点:仅实例化一次,线程是安全的。获取实例的速度快

缺点:类加载时立即实例化对象,可能实例化的对象不被使用,造成内存的浪费。    

六、枚举和懒汉模式相结合

public class EnumSingletonDemo {
 
    private enum EnumHolder{
        INSTANCE;
        private static  EnumSingletonDemo instance=null;
  
        private EnumSingletonDemo getInstance(){
            if(instance ==null) {
                instance = new EnumSingletonDemo();
            }
            return instance;
        }
    }
  
    //实现懒加载
    public static EnumSingletonDemo  getInstance(){
        return EnumHolder.INSTANCE.getInstance();
    }

优点:1、线程安全。且不用加锁。

          2、实现了懒加载 缺点:仍然需要实例非空判断,耗费一定资源        

作者:柠檬五个半