单例模式的7种创建方式

1.饿汉式

public final class SingletonObject1 {
    private static final SingletonObject1 instance = new SingletonObject1();

    private SingletonObject1() {
    } 

    public static SingletonObject1 getInstance() {
        return instance;
    }
}

  饿汉式的创建方法关键在于 instance作为类变量直接得到了初始化,这种方法的优点在于多线程环境下能够百分百地保证同步,在多线程环境下不可能被实例化两次,但是instance若是被加载后很长一段时间后才使用,就意味着instance实例开辟的堆内存会驻留更长的时间,所以更优的创建方式应是伴随着懒加载的。

2.懒汉式

public final class SingletonObject2 {

    private static  SingletonObject2 instance == null;

    private SingletonObject2() {
    }

    public static SingletonObject2 getInstance() {
        if (null == instance)
            instance = new SingletonObject2();
        return instance;
    }
}

  所谓懒汉式就是在使用的时候才去创建,这样就可以避免类在初始化时提前创建,但是这种方式有一个很大的缺点,在多线程的环境下,若一开始因为线程上下文切换的原因,两个线程都通过了null==instance的if循环,这样就是new出两个实例,无法保证单例的唯一性,所以有下面第三种方法。

3.懒汉式+同步方法

public final class SingletonObject3 {

    private static SingletonObject3 instance ;

    private SingletonObject3() {
    }

    public synchronized static SingletonObject3 getInstance() {
        if (null == instance)
            instance = new SingletonObject3();
        return instance;
    }
}

  这种方法通过添加同步控制既满足了懒加载,又满足了instance实例的唯一性,但是,添加了同步控制后getInstance方法的调用是串行化的,效率较低,因此引出第四种创建方式---Double Check方式

4.Double-Check

public final class SingletonObject4 {

Socket socket; //模仿一些资源的实例化

private static SingletonObject4 instance ;

private SingletonObject4() {        this.socket = new Socket();    }

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

  若有两个线程通过了第一个Check循环,进入第二个Check循环是串行化的,只能有一个线程进入,这样当这个线程创建完成后,另外的线程就无法通过第二个循环了,保证了实例的唯一性,随后的线程也不会通过第一个Check循环,也就不会有同步控制的环节了。但是,这种方法也伴随着一个缺点,它可能会引起空指针的异常。

  假设这个单例创建有一些其他的资源,例如Socket、Connection,这些资源在构造函数中也会被实例化,那样在创建单例的时候,就是要实例化自身还有Socket这些资源,那根据JVM的重排序和Happens-before原则,有可能会出现先实例化自身,再去实例化Socket这些资源,若在此时只实例化了自己的情况下,别的线程调用了这个单例中Socket这些资源的方法,而此时它们可能还没有被实例化,这样就会抛出空指针的异常,在此引出第五种创建方法----Volatile+Double-Check

5.Volatile+Double-Check

public final class SingletonObject5 {

    private volatile static SingletonObject5 instance ;

    private SingletonObject5() {
    } 

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

  volatile关键字可以防止重排序的发生,在此不对volatile作详细介绍,通过volatile关键字,这种模式可以说是满足懒加载、多线程下单例的唯一性、安全性的。

6.Holder方式

public final class SingletonObject6{

    private SingletonObject6() {
    }

    private static class InstanceHolder {
        private static  SingletonObject6 instance = new SingletonObject6();
    }

    public static SingletonObject6 getInstance() {
        return InstanceHolder.instance;
    }

}

  Holder这种方式是本人最喜欢的一种创建方式,它借助了类加载的特点,在SingletonObject6中并没有instance的静态成员,而是放置了静态内部类InstanceHolder之中,因此SingletonObject6的初始化过程中并不会实例化instance,当Holder被主动引用的时候才会进行实例化,而在instance被实例化时是会在Java程序编译器中收集至<cliinit>()方法中的,该方法是同步方法,且保证内存的可见性、原子性和顺序性,可以说是饿汉方式的优化版,这种创建方式是最为广泛的方式之一。

7.枚举法

  

public enum  EnumSingleton {
    Instance;
    public void method(){

    }
}

  枚举法是《Effective Java》中作者推荐的方式 ,这种方法极为简单,因为枚举类型本身是final的,不允许被继承,且同样是线程安全的,且只能被实例化一次和不用考虑序列化之类的问题,使用的时候可以直接EnumSingleton.Instance.method()就可以使用了,但是它不能实现懒加载,比如调用其中的静态方法也是会实例化Instance的,读者可以自行测试,但是也可以进行改造,让枚举充当Holder的角色增加懒加载的特性,代码如下

public final class SingletonObject7 {

    private SingletonObject7() {}

    private enum Singleton {
        INSTANCE;

        private final SingletonObject7 instance;

        Singleton(){
            instance = new SingletonObject7();
        }

        public SingletonObject7 getInstance() {
            return instance;
        }
    }

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

原文地址:https://www.cnblogs.com/luonote/p/10347427.html

时间: 2024-11-07 20:01:12

单例模式的7种创建方式的相关文章

设计模式:单例模式的三种创建方式及其各自的优缺点

单例模式: 确保一个类仅仅有一个实例,并提供全局訪问点.在Java中实现单例模式须要私有的构造器,一个静态方法和一个静态变量.确定在性能和资源上 的限制,怎样选择适当的方案来实现单例,以解决多线程的问题. 假设使用多个类载入器,可能导致单例失效产生多个实例.双重检查加锁不适用于1.4及更早版本号的java. 方式1: package singleton; /** * 同步getInstance()方法: * 有点:保证线程安全. * 缺点:性能大大下降 * @author Arvon * */

JAVA中单例模式的几种实现方式

1 线程不安全的实现方法 首先介绍java中最基本的单例模式实现方式,我们可以在一些初级的java书中看到.这种实现方法不是线程安全的,所以在项目实践中如果涉及到线 程安全就不会使用这种方式.但是如果不需要保证线程安全,则这种方式还是不错的,因为所需要的开销比较小.下面是具体的实现代码: 转http://www.cnblogs.com/CodeGuy/p/3580486.html public Class Singleton { private static Singleton instance

单例模式的三种实现方式

一.单例模式的三种实现方式 1. 什么是单例模式 基于某种方法,实例化多次,得到同一个实例/对象 2. 为什么用单例模式 实例化多次,得到的对象属性内容都一样时,应该将这些对象指向同一个内存,即同一个实例,来节省内存空间 1. 实现单例模式方式一:类内部定义类方法实现 实现方法:类中定义了一个类方法 # 未单例模式前 import setting class Mysql: def __init__(self,ip,port): self.ip=ip self.port=port @classme

Java---11---多线程的两种创建方式

多线程的两种创建方式: 下面这些东西是API文档中的: public class Thread extends Object implements Runnable 线程 是程序中的执行线程.Java 虚拟机允许应用程序并发地运行多个执行线程. 每个线程都有一个优先级,高优先级线程的执行优先于低优先级线程.每个线程都可以或不可以标记为一个守护程序.当某个线程中运行的代码创建一个新 Thread 对象时,该新线程的初始优先级被设定为创建线程的优先级,并且当且仅当创建线程是守护线程时,新线程才是守护

2019年6月14日 Web框架之Django_07 进阶操作(MTV与MVC、多对多表三种创建方式、前后端传输数据编码格式contentType、ajax、自定义分页器)

摘要 MTV与MVC 多对多表三种创建方式 ajax ,前后端传输数据编码格式contentType 批量插入数据和自定义分页器 一.MVC与MTV MVC(Model View Controller 模型-视图-控制器)是一种Web架构的模式,所谓MVC就是把web应用分为模型(M),控制器(C),视图(V)三层:他们之间以一种插件似的,松耦合的方式连接在一起. 模型负责业务对象与数据库的对象(ORM),视图负责与用户的交互(页面),控制器(C)接受用户的输入调用模型和视图完成用户的请求. M

Python Django 多对多三种创建方式 form组件 cookie和session

一 表中多对多关系三种创建方式 以Book表和Author表多对多关系例 1.第一种方式:全自动(推荐使用):models.ManyToManyField(to='类名') 优点:不需要你手动创建第三张表 缺点:第三张表不是你手动创建的,字段是固定的无法拓展 class Book(models.Model): title = models.CharField(max_length=32) price = models.DecimalField(max_digits=8,decimal_place

多对多三种创建方式、forms组件、cookies与session

多对多三种创建方式.forms组件.cookies与session 一.多对多三种创建方式 1.全自动 # 优势:不需要你手动创建第三张表 # 不足:由于第三张表不是你手动创建的,也就意味着第三张表字段是固定的无法做扩展 class Book(models.Model): title = models.CharField(max_length=32) price = models.DecimalField(max_digits=8,decimal_places=2) authors = mode

多对多三种创建方式,forms组件,cookie和session

多对多三种创建方式 1.全自动 优点:不需要自己手动创建第三章表缺点:由于第三张表不是自己创建的,也就是说第三张表字段是固定的没有办法做扩展 class Book(models.Model): title = models.CharField(max_length=32) price = models.DecimalField(max_digits=8,decimal_places=2) authors = models.ManyToManyField(to='Author') class Au

Django框架进阶6 多对多三种创建方式, Ajax, Content-Type前后端传输数据编码格式, Ajax发送文件数据, django内置的序列化功能, Ajax结合sweetalert实现删除二次确认, 批量插入数据, 自定义分页器, ajax结合sweetalert实现删除二次确认

多对多三种创建方式 1.全自动(较为常用) class Book(models.Model): title = models.CharField(max_length=32) authors = models.ManyToManyField(to='Author') # orm就会自动帮你创建第三张表 class Author(models.Model): name = models.CharField(max_length=32) ''' 好处:第三张表自己创建 不足之处:第三张表无法扩展额外