单例模式的8种写法

简介: 单例模式的8种写法

1、饿汉式,线程安全

public class Singleton {

    /**
     * 优点:写法简单,类装载的时候完成实例化,线程安全
     * 缺点:类装载的时候就完成实例化,没有Lazy Loading,如果没有被使用,会造成内存浪费
     */
    private static final Singleton INSTANCE = new Singleton();

    public static Singleton getInstance(){
        return INSTANCE;
    }

}

2、饿汉式,线程安全

public class Singleton {

    /**
     * 与第一种方式类似,只是将实例化过程放在静态块中。不知道算不算一种写法。
     * 优点:写法简单,类装载的时候完成实例化,线程安全
     * 缺点:类装载的时候就完成实例化,没有Lazy Loading,如果没有被使用,会造成内存浪费
     */
    private static Singleton INSTANCE;
    
    static{
        if(INSTANCE == null){
            INSTANCE = new Singleton();
        }
    }
    
    public static Singleton getInstance(){
        return INSTANCE;
    }
}

3、懒汉式,线程不安全

public class Singleton {

    /**
     * 优点:Lazy Loading。避免多余的内存开销。
     * 缺点:线程不安全,多线程环境下容易产生多个实例。
     */
    private static Singleton INSTANCE;

    public static Singleton getInstance(){
        if(INSTANCE == null){
            INSTANCE = new Singleton();
        }
        return INSTANCE;
    }
}

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

public class Singleton {

    /**
     * 优点:Lazy Loading。避免多余的内存开销。synchronized线程同步,保证了线程安全。
     * 缺点:synchronized将方法串行化,效率太低
     */
    private static Singleton INSTANCE;

    public static synchronized Singleton getInstance(){
        if(INSTANCE == null){
            INSTANCE = new Singleton();
        }
        return INSTANCE;
    }
}

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

public class Singleton {

    /**
     * 优点:Lazy Loading。避免多余的内存开销。改良同步方法效率低的问题。
     * 缺点:synchronized同步代码块,无法保证线程同步。如果,线程A在判断INSTANCE == null还未执行完毕,另一个线程已经return出一个实例,那么将会产生多个实例。
     */
    private static Singleton INSTANCE;

    public static Singleton getInstance(){
        if(INSTANCE == null){
            synchronized(Singleton.class){
                INSTANCE = new Singleton();
            }
        }
        return INSTANCE;
    }
}

6、懒汉式,线程安全(双重检查)

public class Singleton {

    /**
     * 优点:线程安全,Lazy Loading,效率高
     * 缺点:使用volatile,jdk1.5版本之前无法完全避免重排序锁导致的问题。jdk1.5之前无法保证线程安全。(现在都java12了, 问题不大)
     */
    private static volatile Singleton INSTANCE;

    public static Singleton getInstance(){
        if(INSTANCE == null){
            synchronized(Singleton.class){
                if(INSTANCE == null){
                    INSTANCE = new Singleton();
                }
            }
        }
        return INSTANCE;
    }
}

7、静态内部类,线程安全(推荐使用)

public class Singleton {

    /**
     * 优点:Lazy Loading,线程安全,写法简单
     * 缺点:需要额外处理序列化问题,否则一个序列化的对象实例在每次反序列化后都会创建一个新的实例;传参复杂。
     */
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }

    public static final Singleton getInstance(){
        return SingletonHolder.INSTANCE;
    }
}

8、枚举,线程安全(推荐使用)

public enum  Singleton {


    /**
     * 线程安全,避免反序列化问题。推荐使用
     */
    INSTANCE;
    public void getInstance(){

    }

//    public static void main(String[] args) {
//        Singleton.INSTANCE.getInstance();
//    }
}

为什么说枚举能保证线程安全,而且能保证单例?

通过javap反编译Singleton枚举。

INSTANCE最终被声明为static final。

JVM类加载过程中,虚拟机会保证一个类的()方法在多线程环境中被正确地加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的()方法,其他线程都需要阻塞等待,直到活动线程执行()方法完毕。如果在一个类的()方法中有耗时很长的操作,就可能造成多个进程阻塞(需要注意的是,其他线程虽然会被阻塞,但如果执行()方法后,其他线程唤醒之后不会再次进入()方法。同一个加载器下,一个类型只会初始化一次。),在实际应用中,这种阻塞往往是很隐蔽的。

所以枚举既保证了线程的安全性,又保证了实例的单例性。

总结:单例模式使用哪种写法,最终取决于业务的需要(如是否需要频繁的创建或者销毁对象)。推荐静态内部类和枚举两种写法。

相关文章
|
1月前
|
设计模式 安全
单例模式的几种写法
【10月更文挑战第10天】
94 61
|
安全
单例模式的写法
单例模式的写法
|
6月前
单例模式例子
单例模式例子
|
SQL 设计模式 安全
6. 单例模式有几种写法?
6. 单例模式有几种写法?
103 0
6. 单例模式有几种写法?
|
设计模式 安全 Java
设计模式-Singleton单例模式详解以及8种写法
设计模式-Singleton单例模式详解以及8种写法
|
SQL 设计模式 安全
|
设计模式 SQL 存储
【面试干货】单例模式的七种写法
【面试干货】单例模式的七种写法
175 0
【面试干货】单例模式的七种写法
|
设计模式 安全 Java
单例模式的七种写法,你都知道吗?
单例模式的七种写法,你都知道吗?
192 0
单例模式的七种写法,你都知道吗?
|
设计模式 安全
论单例的写法
有一回对我说道,“你学过设计模式么?”我略略点一点头。他说,“学过,……我便考你一考.设计模式的单例,怎样写的?”我想,讨饭一样的人,也配考我么?便回过脸去,不再理会.孔乙己等了许久,很恳切的说道,“不能写罢?……我教给你,记着!这些模式应该记着.将来当面试官的时候,面试要用.”我暗想我和面试官的等级还很远呢又好笑,又不耐烦,懒懒的答他道,“谁要你教?”孔乙己显出极高兴的样子,将两个指头的长指甲敲着柜台,点头说,“单例有六种写法,你知道么?
189 0