设计模式——单例模式

单例模式:

                为什么用单例模式?什么时候用?怎么用?

             单例模式好处

              ①:单例模式保证了系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能;

              ②:当想实例化的时候,必须要记住使用相应的获取对象方法,而不是使用new;

              ③:单例模式使用场景:需要频繁的进行创建和销毁的对象、创建对象和耗时过多或者消耗资源过多(即:重量级对象),但又经常使用到的对象。如:工具对象、频繁访问数据库或者文件对象(如:数据源、session工厂等)

 

下面我们对单例模式详细分析(写的不对的地方,希望大家来指正)

               1):饿汉式(静态常量)

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

                      缺点:在类装载时的时候就完成实例化,没有达到Lazy Loading的效果。如果从始至终都没用到过这个类,则会造成内存的浪费。

                      总结:这种单例模式可用,可能造成内存浪费 

package singleton;

public class EvilMan1 {

    public static void main(String[] args) {
        //测试
        Evil evil1 = Evil.getInstance();
        Evil evil2 = Evil.getInstance();
        System.out.println(evil1.hashCode());
        System.out.println(evil2.hashCode());
        //输出 366712642   366712642

    }

}

//饿汉式静态变量
class Evil{
    //1.构造器私有化,外部能new
    private Evil() {
    }
    
    //2.本类内部创建对象实例
    private final static Evil evil = new Evil();
    
    
    //3.提供一个共有的静态方法,返回实例对象
    public static Evil getInstance() {
        return evil;
    }
}

      

        2):饿汉式(静态代码块)

                      优点:这种方式和上面的方式类似,只不过将类实例化的过程放在静态代码块中,优缺点和上面一样

package singleton;

public class EvilMan2 {

    public static void main(String[] args) {
        //测试
        Evil evil1 = Evil.getInstance();
        Evil evil2 = Evil.getInstance();
        System.out.println(evil1.hashCode());
        System.out.println(evil2.hashCode());
    }

}

//饿汉式静态变量
class Evil2{
    //1.构造器私有化,外部能new
    private Evil2() {
    }
    
    //2.本类内部创建对象实例
    private  static Evil2 evil;
    
    //在静态代码方法中返回实例对象
    static {
        evil = new Evil2();
    }
    
    
    //3.提供一个共有的静态方法,返回实例对象
    public static Evil2 getInstance() {
        return evil;
    }
}

 

               3):懒汉式(线程不安全)

                      特点:起到了Lazy Loading的效果,但是只能在单线程下使用。如果在多线程下,一个进程进入了判断语句,还没来得急往下执行,另一个线程也通过了这个判断语句,这是便会产生多个实例。所以在多线程环境下不可以使用这种模式

                      总结在实际开发中,不要用这种模式

package singleton;

public class EvilMan3 {

    public static void main(String[] args) {
        //测试
        Evil evil1 = Evil.getInstance();
        Evil evil2 = Evil.getInstance();
        System.out.println(evil1.hashCode());
        System.out.println(evil2.hashCode());
    }

}

//饿汉式(线程不安全)
class Evil3{

    
    //2.本类内部创建对象实例
    private  static Evil3 evil;
    
    private Evil3() {}
    
    //3.提供一个共有的静态方法,返回实例对象
    public static Evil3 getInstance() {
        if (evil == null) {
            evil = new Evil3();
        }
        return evil;
    }
}

 

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

                     总结:如果在方法中加上synchronized,倒是可以解决线程不安全问题,但是效率太低,每个线程执行到getInstance()方法都要进行同步,而这方法只执行一次就够了,后面想要获得该类实例,直接return就可以了。方法进行同步效率太低。在实际开发中,不推荐使用

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

                     总结:要是同步代码块,如下面,就不同线程同步了,类似于懒汉第一种,开发中,不能使用

class Evil3{

    
    //2.本类内部创建对象实例
    private  static Evil3 evil;
    
    private Evil3() {}
    
    //3.提供一个共有的静态方法,返回实例对象
    public static Evil3 getInstance() {
        if (evil == null) {
            synchronized (Evil3.class) {
                evil = new Evil3();
            }
        }
        return evil;
    }
}

 

               6):双重检查

                      总结:我们进行了两次if(evil  == null)判断,这样就可以保证线程安全,在使用过程中只能执行一次实例化代码操作,后面在访问时,直接返回实例化对象,也避免了反复方法景行同步。其次,线程安全,延时加载,效率较高,在实际开发过程中,推荐使用这种开发模式

class Evil4{
    
    private  static volatile  Evil4 evil;
    
    private Evil4() {}
    
    public static Evil4 getInstance() {
        if (evil == null) {
            synchronized (Evil3.class) {
                if (evil == null) {
                    evil = new Evil4();
                }
            }
        }
        return evil;
    }
}

 

               7):静态内部类

                      总结

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

                               2):静态内部类方式在Evil3类呗装载时并不会立即实例化,而是在需要实例化时,调用getInstance()方法,才会装载Evil3Instance类,从而完成Evil3的实例化。

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

                               4):避免了线程不安全,利用静态内部类特点实现延时加载,效率高

                               5):推荐使用

class Evil3{

    private  static volatile Evil3 evil;
    
    private Evil3() {}
    
    //写一个静态内部类,该类有一个静态属性Evil3
    public static class Evil3Instance{
        private static final Evil3 I_EVIL3 = new Evil3();
    }
    
    //提供一个静态的公有方法
    public static synchronized Evil3 getInstance() {
        return Evil3Instance.I_EVIL3;
    }
}

               8):枚举

                      总结

                               1):不仅能避免多线程同步问题,而且还能防止反序列化重新创建对象

                               3):推荐使用

public class EvilMan4 {

    public static void main(String[] args) {
        //测试
        Evil4 evil1 = Evil4.instance;
        Evil4 evil2 = Evil4.instance;
        System.out.println(evil1.hashCode());
        System.out.println(evil2.hashCode());
        evil1.getInstance();
    }

}


enum  Evil4{
    instance;
    public  void getInstance() {
        System.out.println("ok");
    }
}

总结:对于单例模式的八种模式,推荐饿汉式、双重检测、静态内部类、枚举


版权声明:本文为qq_39547747原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。