`

单例模式总结

阅读更多
单例模式总结
单实例的正确写法
并文章属于Java并发编程实战中例子。但结合实际场景进行了阐述。
通常,我们如果写一个单实例模式的对象,一般会这样写:
写法一:
 
Java代码  
public class Singleton {  
    private static final Singleton instance = new Singleton();  
    /** 
     * 防止其他人new对象 
     */  
    private Singleton(){  
        System.out.println("init");  
    }  
    public static Singleton getInstance(){  
        return instance;  
    }  
}  
 
 
这种方式叫饥饿式单实例,意思是说,不管你用不用这个类的方法,我都把这个类需要的一切资源都分配好。但这样写有一个问题,就是如果这类需要的资源比较多,在系统启动的时候,就会很慢。
因此要求有懒汉式单实例,于是就出现了第二中写法,
写法二:
 
Java代码  
public class Singleton {  
    private static Singleton instance = null;  
    /** 
     * 防止其他人new对象 
     */  
    private Singleton(){  
        System.out.println("init");  
    }  
    public static Singleton getInstance(){  
        if(instance == null){  
            instance = new Singleton();  
        }  
        return instance;  
    }  
}  
 这种方式叫懒汉式单实例,即通常所说的延迟加载。这样,在系统启动的时候,不会加载类所需要的各种资源,只有真正使用的时候才去加载各种资源。
 
但这种方法马上就可以看出问题,因为在多线程情况下,可能会导致重复初始化的问题(不明白这个道理,那您需要补充一下同步及多线程知识了)。于是有了改进版,即目前网上比较流行的写法。
写法三:
 
Java代码  
public class Singleton {  
    private static Singleton instance = null;  
    /** 
     * 防止其他人new对象 
     */  
    private Singleton(){  
        System.out.println("init");  
    }  
    public static synchronized Singleton getInstance(){  
        if(instance == null){  
            instance = new Singleton();  
        }  
        return instance;  
    }  
}  
 加上关键字synchronized,可以保证只有一个线程在执行这个方法。这个方法至此应该说是比较完美的了,但是,专家不这么认为,在高并发多线程的访问系统中,synchronized关键字会让程序的吞吐量急剧下降,因此,在高并发系统中,应该尽量避免使用synchronized锁。
但这并不能难住我们聪明的软件工程师,有人便写出了双重锁的程序。方法如下:
写法四:
Java代码  
public class Singleton {  
    private static Singleton instance = null;  
    /** 
     * 防止其他人new对象 
     */  
    private Singleton(){  
        System.out.println("init");  
    }  
    public static  Singleton getInstance(){  
        if(instance == null){  
            synchronized(Singleton.class){  
                if(instance == null){  
                    instance = new Singleton();  
                }  
            }  
        }  
        return instance;  
    }  
}  
这样,通常获得单实例引用是没有锁的,只有第一次初始化时才会加锁,而且如果多个线程进入临界区区后,理论上只有第一个进入临界区的线程才会初始化对象,之后进入临界区的线程因为之前的线程已经初始化,就不会再次进行初始化。
但专家怎么说呢?这个代码有问题。首先,这个程序对同步的应用很到位,即当进入synchronied区,只有一个线程在访问Singleton类。但却忽略了变量的可见性。因为在没有同步的保护下,instance的值在多个线程中可能都是空的,因为即便第一个线程对类进行了初始化,并把类的引用赋值给了instance变量,但也不能保证instance变量的值对其他线程是可见的,因为变量instance没有采用同步的机制。
在java5之后,可以在instance前面添加volatile关键字来解决这个问题,但是这种双重锁的方式已经不建议使用。
 
那么,看看大师推荐的写法吧,见 Java Concurrency In Practice的List 16.6代码:
写法五:
Java代码  
public class Singleton {  
    private static class SingletonHolder {  
        public static Singleton resource = new Singleton();  
    }  
    public static Singleton getResource() {  
        return  SingletonHolder.resource ;  
    }  
      
    private Singleton(){  
          
    }  
}  
 
综上各种写法,发现写法一虽然在启动时会让系统启动的慢一些,但却不失为一种简洁而高效的写法,当然,如果确实对系统启动时的速度要求高的话,则应该考虑写法五了。
另外,其实单实例方法还有好多种,在effective Java中有写到:
写法六:
Java代码  
public class Singleton {  
    public static final Singleton INSTANCE = new Singleton();  
      
    private Singleton(){}  
      
    public void method(){  
        //...  
    }  
    public static void main(String[] a){  
        //调用方法。  
        Singleton.INSTANCE.method();  
    }  
}  
 写法七:
Java代码  
/** 
 * 利用枚举巧妙创建单实例 
 */  
public enum Singleton {  
    INSTANCE;  
    public void method(){  
        //...  
    }  
    public static void main(String[] a){  
        //调用方法。  
        Singleton.INSTANCE.method();  
    }  
}  
 另外,双重锁的方式,在加上volatile关键字后,也是高效安全的写法。
写法八:
Java代码  
public class Singleton {  
    private static volatile Singleton instance = null;  
    /** 
     * 防止其他人new对象 
     */  
    private Singleton(){  
        System.out.println("init");  
    }  
    public static  Singleton getInstance(){  
        if(instance == null){  
            synchronized(Singleton.class){  
                if(instance == null){  
                    instance = new Singleton();  
                }  
            }  
        }  
        return instance;  
    }  
}  
 
 
其实,在今天spring大行其道的天下,单实例需求已经不多,spring中的bean默认都是单实例。但是要做一些app程序或者开发一个产品时,这种模式还是很重要的。综上所述,我个人比较推荐写法五和写法一,写法七怎么看着也别扭。


转自:http://hi.baidu.com/donlian/item/b746f074655ee93e704423f0
分享到:
评论

相关推荐

    43丨单例模式(下):如何设计实现一个集群环境下的分布式单例模式?1

    总结来说,单例模式在进程内保证对象的唯一性,但在分布式环境中需要额外的策略来实现跨进程的唯一性。线程唯一的单例则关注于在单个线程内的对象唯一,允许不同线程拥有各自的实例。在实际应用中,应根据系统的需求...

    使用C++11实现线程安全的单例模式

    总结来说,C++11通过`std::mutex`和`std::call_once`等工具,为实现线程安全的单例模式提供了强大支持。这种实现方式既简单又高效,避免了传统实现(如双重检查锁定)可能存在的性能问题。在实际项目中,根据具体...

    java单例模式实例

    总结起来,Java中的单例模式有多种实现,每种都有其优缺点。开发者应根据项目需求选择适合的实现方式,比如对性能要求较高的场景可以选择静态内部类或枚举,而对内存占用敏感的场景则可能更适合饿汉式。通过学习和...

    单例模式详解 1. 什么是单例模式? 1.1 单例模式的核心要素 1.2 为什么需要单例模式? 2. 单例模式的实现方式 2.1 饿汉式(静态常量) 2.2 饿汉式(静态代码块) 2.3 懒汉式(线程

    单例模式详解 1. 什么是单例模式? 1.1 单例模式的核心要素 1.2 为什么需要单例模式? 2. 单例模式的实现方式 2.1 饿汉式(静态常量) 2.2 饿汉式(静态代码块) 2.3 懒汉式(线程不安全) 2.4 懒汉式(线程安全,...

    7种单例模式

    单例模式是软件设计模式中的一种经典模式,其主要...总结起来,单例模式有多种实现方式,每种方式都有其适用场景和优缺点。在实际应用中,开发者应根据项目需求选择合适的单例实现,并注意线程安全和反序列化等问题。

    使用单例模式实现计数器

    总结来说,单例模式在实现计数器时,可以确保计数器的全局唯一性,同时提供了一种线程安全的方式来管理和访问这个计数器。这种模式在需要全局共享资源或状态,如日志服务、缓存管理、数据库连接池等场景中尤为适用。...

    Java 单例模式 工具类

    总结,Java中的单例模式在设计和实践中具有重要的地位。通过`SingletonFactory`工具类,我们不仅可以方便地使用各种单例模式,还可以对自定义的单例进行统一管理和访问,提高了代码的可维护性和灵活性。在实际开发中...

    单例模式应用场景

    通过对上述应用场景的分析,我们可以总结出单例模式适用的几种情况: - **资源共享:** 当需要频繁访问某个资源(如日志文件、配置文件等)时,采用单例模式可以减少资源操作所带来的性能损耗。 - **控制资源:** ...

    OOP单例模式和工厂模式

    ### OOP中的单例模式与工厂模式 #### 单例模式和工厂模式概述 在面向对象编程(OOP)领域,设计模式被视为一种通用的解决方案,用于解决在软件设计过程中遇到的常见问题。这些模式提供了经过验证的设计策略,有助...

    单例模式PHP实现代码类.zip

    总结来说,单例模式在PHP中主要用于管理和控制那些需要全局访问且资源消耗较大的对象。虽然它有一定的局限性,但在特定场景下,比如资源管理,它可以提供高效的解决方案。理解并熟练运用单例模式是提升PHP编程能力的...

    单例模式,single

    ### 单例模式详解 #### 概述与应用场景 单例模式是一种常用的设计模式,它的主要目的是确保某个类仅有一个实例,并提供一个全局访问点。这种模式非常适合那些在整个应用程序生命周期中只需要一个实例的对象,例如...

    设计模式单例模式

    总结来说,单例模式有多种实现方式,包括懒汉式、饿汉式和同步枷锁等。开发者需要根据实际需求,平衡性能和线程安全,选择最适合的实现策略。在Java中,推荐使用双重检查锁定的懒汉式或静态内部类的饿汉式,它们在...

    23钟设计模式之单例模式

    总结来说,单例模式在Java中的实现涉及到多线程同步、内存模型以及性能优化等多个方面。理解并熟练掌握各种单例模式的实现方式,有助于我们编写出更加健壮、高效的代码。同时,设计模式的应用不仅仅局限于单例,还有...

    Java单例模式的全面总结

    总结来说,单例模式通过限制类的实例化次数,确保了全局唯一性,从而简化了对共享资源的管理。不同的实现方式各有优缺点,如懒汉式注重延迟加载,饿汉式强调线程安全,而登记式则提供了多单例管理的灵活性。选择哪种...

    单例模式(饿汉模式、懒汉模式、DCL单例模式、枚举)

    总结,选择哪种单例模式取决于具体需求。饿汉模式适合对性能敏感且始终需要单例的情况;懒汉模式适用于内存有限且初始化耗时的场景;DCL单例模式在性能和线程安全之间找到了平衡;而枚举单例模式则是最安全且推荐的...

    Java单例模式应用研究.pdf

    ### Java单例模式应用研究 #### 一、单例模式概述 单例模式(Singleton Pattern)作为一种最基本的创建型设计模式,其主要目的是控制一个类的实例化过程,确保在整个应用程序中仅存在一个实例,并且该实例能够被全局...

    单例模式---初学 优点--缺点

    总结来说,单例模式是一种权衡设计,它在保证资源的有效管理和控制的同时,也可能带来一定的负面影响。在实际开发中,应根据具体需求和场景谨慎使用。在Java等语言中,可以使用双重检查锁定、静态内部类等策略优化...

    单例模式_命令模式

    总结来说,单例模式保证了游戏规则管理器的唯一性,命令模式使得游戏逻辑可以通过命令对象进行灵活控制,而反射机制则实现了根据配置动态加载和执行命令。这种设计组合在`MyFirstGame v3.0`中展现了强大的灵活性和可...

    单例模式(singleton)

    总结来说,单例模式是一种重要的设计模式,用于控制类实例的数量,以优化资源管理和提高效率。在实际开发中,我们需要根据具体需求选择合适的实现方式,同时注意线程安全问题,确保单例模式的正确性和稳定性。

Global site tag (gtag.js) - Google Analytics