Java单例模式的七种实现

1.饿汉式

//final不允许类被继承
public final class Singleton {
    //实例化变量
    private byte[] data=new byte[1024];

    //直接初始化
    private static Singleton instance=new Singleton();

    //私有构造函数,不允许外部new
    private Singleton(){
    }

    public static Singleton getInstance(){
        return instance;
    }
}

  如果主动使用Singleton类,instance实例将直接完成创建,包括其中的实例变量都会得到初始化,但是instance可能被加载很长一段时间后才被使用,instance实例开辟的堆内存会驻留更久的时间,如果说一个类的成员变量不多,且占用内存资源较少,可以使用饿汉式,总结它可以保证多个线程下唯一实例,getInstance方法性能较高,但是无法进行懒加载

2.懒汉式

//final不允许类被继承
public final class Singleton {
    //实例化变量
    private byte[] data=new byte[1024];

    private static Singleton instance=null;

    //私有构造函数,不允许外部new
    private Singleton(){
    }

    //等到需要使用时进行创建
    public static Singleton getInstance(){
        if (instance==null){
            instance=new Singleton();
        }
        return instance;
    }
}

  该方法在多线程环境下不能保证单例的唯一性

3.懒汉+同步方法

//final不允许类被继承
public final class Singleton {
    //实例化变量
    private byte[] data=new byte[1024];

    private static Singleton instance=null;

    //私有构造函数,不允许外部new
    private Singleton(){
    }

    //等到需要使用时进行创建
    public static synchronized Singleton getInstance(){
        if (instance==null){
            instance=new Singleton();
        }
        return instance;
    }
}

  可以保证在多线程环境下单例的唯一性,但是synchronied关键字会导致在同一时刻方法只能被一个线程所访问,性能低下。

4.双重锁检查

//final不允许类被继承
public final class Singleton {
    //实例化变量
    private byte[] data=new byte[1024];

    private static Singleton instance=null;

    //私有构造函数,不允许外部new
    private Singleton(){
    }

    //等到需要使用时进行创建
    public static Singleton getInstance(){
        if (instance==null){
            synchronized (Singleton.class){
                if (instance==null) instance=new Singleton();
            }
        }
        return instance;
    }
}

  这段代码看起来很完美,很可惜,它是有问题。主要在于instance = new Singleton()这句,这并非是一个原子操作,事实上在 JVM 中这句话大概做了下面 3 件事情:

  1.给 instance 分配内存
  2.调用 Singleton 的构造函数来初始化成员变量
  3.将instance对象指向分配的内存空间(执行完这步 instance 就为非 null 了)
  但是在 JVM 的即时编译器中存在指令重排序的优化。也就是说上面的第二步和第三步的顺序是不能保证的,最终的执行顺序可能是 1-2-3 也可能是 1-3-2。如果是后者,则在 3 执行完毕、2 未执行之前,被线程二抢占了,这时 instance 已经是非 null 了(但却没有初始化),所以线程二会直接返回 instance,然后使用,然后顺理成章地报错。

5.volatile+双重锁检查

//final不允许类被继承
public final class Singleton {
    //实例化变量
    private byte[] data=new byte[1024];

    //禁止指令重排序
    private volatile static Singleton instance=null;

    //私有构造函数,不允许外部new
    private Singleton(){
    }

    //等到需要使用时进行创建
    public static Singleton getInstance(){
        if (instance==null){
            synchronized (Singleton.class){
                if (instance==null) instance=new Singleton();
            }
        }
        return instance;
    }
}

  加上volatile禁止了指令的重排许操作。满足多线程下的单例,懒加载,获取实例的高效性。

6.Holder方式

//final不允许类被继承
public final class Singleton {
    //实例化变量
    private byte[] data=new byte[1024];

    //私有构造函数,不允许外部new
    private Singleton(){
    }

    //在静态内部类中持有singleton的实例,可以被直接初始化
    private static class Holder{
        private static Singleton instance=new Singleton();
    }

    public static Singleton getInstance(){
        return Holder.instance;
    }
}

  Holder类中定义了Singleton的静态变量,并且直接进行了实例化,当Holder被主动引用的时候会创建Singleton实例。Holder方式的单例模式是最好的设计之一,也是目前用的比较广泛的设计之一。

7.枚举方式

  枚举类型不允许被继承,同样是线程安全的且只能被实例化一次,但是枚举类型不能够懒加载,对Singleton主动使用,比如调用其中的静态方法则INSTANCE会立即得到实例化。

public enum Singleton{
    INSTANCE;
    private byte[] data=new byte[1024];
    
    Singleton(){
        System.out.println("INSTANCE will be initialized immediately");
    }
    
    public static void method(){
        //调用该方法会主动使用Singleton,INSTANCE实例将会被实例化
    }
    
    public static Singleton getInstance(){
        return INSTANCE;
    }
}

猜你喜欢

转载自www.cnblogs.com/xiaobaituyun/p/10821565.html
今日推荐