java设计模式--单例模式

单例模式

单例模式在Java设计模式中属于非常重要的一种模式,意在保证一个类只有唯一一个实例,并且向整个系统提供一个访问它的全局访问点。

 

单例模式的特点

  • 单例类只能有一个实例。

  • 单例类必须自己创建自己的唯一实例。

  • 单例类要为其他所有对象提供这一个唯一实例。

 

  单例模式比较常见的有饿汉式、懒汉式、双重检查加锁,还有比较有特点的静态内部类和单例模式,下面我们通过每一种类型的代码来讲解:

饿汉式

public class EagerSingleton {
    private static EagerSingleton eagerSingleton = new EagerSingleton();
    /*构造器*/
    private EagerSingleton() {
    }

    ;
    /*    静态工厂方法*/
    public static EagerSingleton getInstance() {
        return eagerSingleton;
    }
}

  饿汉式,顾名思义为很饿,想快一点得到自己想要的东西,也就是在类被加载的时候(即实例化对象的时候或者通过类名调用静态变量的时候),静态变量eagerSingleton就会调用EagerSingleton类的私有构造器创建类的实例,不管是否要使用到该类的实例都会先创建,如果调用getInstance方法则会得到EagerSingleton类的实例,由于静态变量只会初始化一次,所以无论在何时何地调用getInstance方法都会得到该类的唯一实例。

  饿汉式是典型的以空间换时间的做法,在类被加载的时候就会创建该类的实例放于内存中,这样使得运行时间更短,但是过多的使用饿汉式会大量的占用系统的内存。

懒汉式

 

public class LazySingleton {
    private static LazySingleton lazySingleton = null;

    private LazySingleton() {
    }

    public static synchronized LazySingleton getInstance() {
        if (lazySingleton == null) {
            lazySingleton = new LazySingleton();
        }
        return lazySingleton;
    }

}

  懒汉式,顾名思义懒则不像饿汉式那样在类被加载的时候就创建了类的唯一实例,而是在不得不使用的时候创建类的实例返回,在静态方法getInstance被调用的时候先判断静态变量lazySingleton是否为空,如果为空则创建LazySingleton实例。

  懒汉式是典型的以时间换空间的做法,为了处理多线程环境的调用,在getInstance方法加了synchronized关键字使得该方法是线程安全的,但是降低了整个系统对该类的访问速度,同时,由于在第一次需要使用LazySingleton实例的时候才会创建LazySingleton类的实例,节约了内存空间。

双重检查加锁

  双重检查锁可以既实现线程同步又实现又能够使性能不像懒汉式一样影响这么大。

public class Singleton {
    private volatile static Singleton singleton = null;

    private Singleton() {
    }

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

  双重检查锁的实现在系统调用getInstance方法的时候会先判断singleton是否为空,如果为空才会进入同步方法,然后再进行第二次判断singleton是否为空,如果为空则调用私有的构造器实例化该类,这样在系统调用getInstance方法时就只需要在第一次的时候实例化的时候调用同步块就可以了,这种方式减少了每次调用getInstance都要通过同步方法进行判断的损耗时间,大大了提升了性能。

  同时该方法使用了volatile关键字,在这里volatile关键字一共有两个用途:

  1.   被volatile修饰的变量的值不会在本地线程缓存,也就是说每次要用到singleton变量都要从内存去取,这样在singleton变量被赋值了后另一个线程在下次使用的时候能够马上得到singleton的新值,确保了多线程能够正确处理该变量。

  2.   使用volatile关键字能够禁止指令的重排序,在A线程正在创建对象singleton的时候可能在同时B线程正在判断singleton是否为空,可能导致songleton对象还未初始化但是在B线程中在用该对象做判断导致报错。

静态内部类实现

public class StaticSingleton {
    private StaticSingleton() {
    }

    private static class SingleHolder {
        private static StaticSingleton staticSingleton = new StaticSingleton();
    }

    public static StaticSingleton getInstance() {
        return SingleHolder.staticSingleton;
    }
}

  通过静态内部类实现单例模式有一个好处就是调用getInstance方法的时候直接读取SingleHolder.staticSingleton,导致SingleHoleder类初始化并且实例化StaticSingleton类,这种方式实现不用担心效率的问题,类的延迟加载也得到了实现。

单例枚举类

  最后一种方式是通过枚举类来实现单例模式

public enum EnumSingleton {
    INSTABCE;

    public void SingeletonOperation() {
        System.out.println("SingeletonTest");
    }
}

  使用枚举类来实现单例模式代码简洁,功能完善,并且该方式能够方式序列化反序列化的破坏,同时也能够处理反射攻击,能够保证单例模式的多次实例化。

posted on 2019-05-23 01:00  会飞的小杰  阅读(119)  评论(0)    收藏  举报

导航