zoukankan      html  css  js  c++  java
  • 【Java】设计模型-五种单例模型

    一. 什么是单例模式

    只需要某个类同时保留一个对象,不希望有更多对象,此时,我们则应考虑单例模式的设计。

    单例模式的主要作用是保证在Java程序中,某个类只有一个实例存在。

    单例模式有很多好处,它能够避免实例对象的重复创建,不仅可以减少每次创建对象的时间开销,还可以节约内存空间;

    能够避免由于操作多个实例导致的逻辑错误。如果一个对象有可能贯穿整个应用程序,而且起到了全局统一管理控制的作用,那么单例模式也许是一个值得考虑的选择。

    二. 单例模式的特点

    1. 单例模式只能有一个实例。

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

    3. 单例类必须向其他对象提供这一实例。

    三. 单例模式VS静态类

    在知道了什么是单例模式后,我想你一定会想到静态类,“既然只使用一个对象,为何不干脆使用静态类?”,这里我会将单例模式和静态类进行一个比较。

    1. 单例可以继承和被继承,方法可以被override,而静态方法不可以。

    2. 静态方法中产生的对象会在执行后被释放,进而被GC清理,不会一直存在于内存中。

    3. 静态类会在第一次运行时初始化,单例模式可以有其他的选择,即可以延迟加载。

    4. 基于2, 3条,由于单例对象往往存在于DAO层(例如sessionFactory),如果反复的初始化和释放,则会占用很多资源,而使用单例模式将其常驻于内存可以更加节约资源。

    5. 静态方法有更高的访问效率。

    6. 单例模式很容易被测试。

    几个关于静态类的误解:

    误解一:静态方法常驻内存而实例方法不是。

    实际上,特殊编写的实例方法可以常驻内存,而静态方法需要不断初始化和释放。

    误解二:静态方法在堆(heap)上,实例方法在栈(stack)上。

    实际上,都是加载到特殊的不可写的代码内存区域中。

    静态类和单例模式情景的选择:

    情景一:不需要维持任何状态,仅仅用于全局访问,此时更适合使用静态类。

    情景二:需要维持一些特定的状态,此时更适合使用单例模式。

    单例模型的写法

    1、饿汉模式

    public class Singleton{
        private static Singleton instance = new Singleton();
        private Singleton(){}
        public static Singleton newInstance(){
            return instance;
        }
    }
    

    注解:初试化静态的instance创建一次。如果我们在Singleton类里面写一个静态的方法不需要创建实例,它仍然会早早的创建一次实例。而降低内存的使用率。

    缺点:没有lazy loading的效果,从而降低内存的使用率。

    2、懒汉模式

    public class Singleton{
        private static Singleton instance = null;
        private Singleton(){}
        public static Singleton newInstance(){
            if(null == instance){
                instance = new Singleton();
            }
            return instance;
        }
    }
    

    注解:Singleton的静态属性instance中,只有instance为null的时候才创建一个实例,构造函数私有,确保每次都只创建一个,避免重复创建。
    缺点:只在单线程的情况下正常运行,在多线程的情况下,就会出问题。例如:当两个线程同时运行到判断instance是否为空的if语句,并且instance确实没有创建好时,那么两个线程都会创建一个实例,因此需要加锁解决线程同步问题,实现如下:

    public class Singleton{
        private static Singleton instance = null;
        private Singleton(){}
        public static synchronized Singleton newInstance(){
            if(null == instance){
                instance = new Singleton();
            }
            return instance;
        }
    }
    

    3、双重校验锁 

    public class Singleton {
        private static Singleton instance = null;
        private Singleton(){}
        public static Singleton getInstance() {
            if (instance == null) {
                synchronized (Singleton.class) {
                    if (instance == null) {//2
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    }
    

    注解:只有当instance为null时,需要获取同步锁,创建一次实例。当实例被创建,则无需试图加锁。
    缺点:用双重if判断,复杂,容易出错。

    4、静态内部类

    public class Singleton{
        private static class SingletonHolder{
            public static Singleton instance = new Singleton();
        }
        private Singleton(){}
        public static Singleton newInstance(){
            return SingletonHolder.instance;
        }
    }
    

    这种方式同样利用了类加载机制来保证只创建一个instance实例。它与饿汉模式一样,也是利用了类加载机制,因此不存在多线程并发的问题。不一样的是,它是在内部类里面去创建对象实例。这样的话,只要应用中不使用内部类,JVM就不会去加载这个单例类,也就不会创建单例对象,从而实现懒汉式的延迟加载。也就是说这种方式可以同时保证延迟加载和线程安全。

    5、枚举

    public enum Singleton{
        instance;
        public void whateverMethod(){}    
    }  

    上面提到的四种实现单例的方式都有共同的缺点:

    1)需要额外的工作来实现序列化,否则每次反序列化一个序列化的对象时都会创建一个新的实例。

    2)可以使用反射强行调用私有构造器(如果要避免这种情况,可以修改构造器,让它在创建第二个实例的时候抛异常)。

           而枚举类很好的解决了这两个问题,使用枚举除了线程安全和防止反射调用构造器之外,还提供了自动序列化机制,防止反序列化的时候创建新的对象。因此,《Effective Java》作者推荐使用的方法。不过,在实际工作中,很少看见有人这么写。

    总结

           本文总结了五种Java中实现单例的方法,其中前两种都不够完美,双重校验锁和静态内部类的方式可以解决大部分问题,平时工作中使用的最多的也是这两种方式。枚举方式虽然很完美的解决了各种问题,但是这种写法多少让人感觉有些生疏。个人的建议是,在没有特殊需求的情况下,使用第三种和第四种方式实现单例模式。

  • 相关阅读:
    三次请求(读-改-读)引出nibernate 一级缓存
    算法竞赛入门经典第一、二章摘记
    uva 10905 Children's Game
    uva 11205 The broken pedometer
    uva 10160 Servicing stations
    uva 208 Firetruck
    uva 167 The Sultan's Successors
    zoj 1016 Parencodings
    uva 307 Sticks
    uva 216 Getting in Line
  • 原文地址:https://www.cnblogs.com/zeze/p/9582377.html
Copyright © 2011-2022 走看看