`

唯一对象创建--单例模式

阅读更多

Java中类的实例化是通过new关键字实现的。单例模式目标和作用,就是保证任意时刻获取到的类对象都是同一个。也就是说只能使用一次new关键字创建对象,并把这个对象一直保留下来供程序全局使用。根据不同的使用场景,有三种不同的单例模式实现方式,下面分别进行讲解:

 

单线程下的单例模式

 

单线程下的单例模式是最常见的使用方式,但也经常被错误的用到多线程的情况下。其实现方式很简单:

为了限制外部使用new关键字,必须把构造方法设置为私有private

为了把这个创建的对象一直保留下来,必须使用static的常量指向这个对象;

为了外部能使用这个对象,必须暴露一个public static的方法获取这个对象。

满足这三个条件,实现代码如下:

/**
 * 非线程安全单例模式
 * Created by gantianxing on 2017/10/17.
 */
public class Singleton0 {
 
    private static Singleton0 singleton0= null;
 
    private Singleton0(){
 
    }
 
    public static Singleton0 getInstance(){
        if(singleton0 == null){//多线程情况下,会创建多次
            singleton0 = new Singleton0();
        }
        return singleton0;
    }
}

在单线程的情况下,建议使用这种方式即可。有人会问现在的程序基本都是多线程 这种方式是不是没有价值了。其实不然,程序启动初始化的过程 可以理解为单线程,比如在@PostConstruct修饰的方法中首先调用Singleton0. getInstance ()方法,保证singleton0只被初始化一次,后续多个线程调用Singleton0. getInstance ()就不存在新对象创建,其实这种场景也可以使用第二种方式:

 

静态初始化型单例模式

 

在多线程环境里,使用第一种方式,会存在多次执行singleton0 = new Singleton0();

这段代码创建对象,破坏了单例模式的定义。换句话说,上述第一种方式 如果在单线程环境下是单例模式,如果在多线程环境下就是非单例模式,不能使用。下面我们来第二种单例模式实现,这种方式是静态变量初始化实现的单例模式:

 

/**
 * 静态初始化型 线程安全单例
 * Created by gantianxing on 2017/10/17.
 */
public class Singleton1 {
    private static final Singleton1 singleton1 = new Singleton1();
 
    private Singleton1(){
 
    }
 
    public static Singleton1 getInstance(){
        return  singleton1;
    }
}
 

 

这种方式没有第一次初始化线程安全问题,程序启动时已经完成第一次初始化,后续多线程下每次取到的都是统一实例。但如果初始化时间较长,可能会影响程序启动。如果初始化时间不长,建议是用这种方式,反之采用第三种方式 进行延迟初始化。

 

双重检查加锁(DCL)单例模式

 

其实第一种方式就是延迟初始化方式,在第一次使用的时候初始化,但在多线程的情况下无法保证只初始化一次。最简单的保证同步的方式,是直接在getInstance()方法前加synchronized,但在多线程环境下有不必要的性能开销,其实只要能保证第一次new创建对象时同步即可。具体实现方式:

public class Singleton2 {
 
    //注意必须是volatile修饰,保证多线程下数据的可见性
    private volatile static Singleton2 singleton2 = null;
 
    private Singleton2(){
 
    }
 
    public static Singleton2 getInstance(){
        if(singleton2 == null){//第一重检查
            synchronized(Singleton2.class){//类同步
                if(singleton2 == null){//第二重检查
                    singleton2 = new Singleton2();
                }
            }
        }
        return singleton2;
    }
}
 

 

双重检查加锁:第一步 第一次检查 检查singleton2是否为空;第二步 同步 如果为空执行同步代码块;第三步 第二次检查 由于有多个线程情况下有可能有多个线程等待锁,当第一个线程执行完成后,其他等待锁的线程都会依次执行,所以必须进程第二次检查,如果singleton2不为空,则不必再次创建。

 

另外,为了保证多线程之间数据及时可见性,必须使用 volatile修饰成员变量singleton2

 

这就是经典的双重检查加锁实现,这里的使用的synchronized加锁,当然也可以使用Lock新锁api。这种实现方式可以在多线程环境下,对单例对象进行延迟实例化。

 

延长初始化占位类

 

 

延长初始化占位类:是对第二种方式的一种延迟初始化版本,具体是采用静态内部类的静态变量进行初始化。其原理是利用首次访问静态内部类时,只有一个线程加载该内部类,从而保证线程安全,具体实现如下:

public class Singleton4 {
 
   public class Singleton4 {
    private static class InnerHolder{
        public static Singleton4 singleton4 = new Singleton4();
    }
    private Singleton4(){

    }
    public static Singleton4 getInstance(){
        return InnerHolder.singleton4;
    }
}

这种延迟初始化单例模式比第三种DCL方式更加优雅,而且性能更好。在多线程环境下,建议使用这种方式。

 

关于java中四种常见的单例模式实现方式,以及使用场景就总结到这里,可以根据项目具体情况灵活选择。

 

最后提下,单例模式不仅仅用来创建一个普通对象,同样可以用于创建单例的Map、List等。

0
0
分享到:
评论
2 楼 moon_walker 2017-10-18  
somefuture 写道
写的不错 不过现在流行的单利模式是用枚举

枚举我理解其实跟第二种方式类似,都属于静态初始化,在程序启动时就已经初始化了,所以没列出来。如果要延迟初始化,还是得使用第三种方式。
1 楼 somefuture 2017-10-18  
写的不错 不过现在流行的单利模式是用枚举

相关推荐

    Java设计模式之单例模式

    单例模式类图 单例模式归类 单例模式的应用场景 单例模式解决的问题 单例模式的实现方式 单例模式实现方式对比 单例模式的概念 单例模式,顾名思义就是只有一个实例,并且由它自己负责创建自己的对象,这个类提供了...

    单例模式 Singleton Pattern

    单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。 这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个...

    C# 设计模式系列教程-单例模式

    2. 单例模式主要有3个特点,:  2.1 单例类确保自己只有一个实例。  2.2 单例类必须自己创建自己的实例。  2.3 单例类必须为其他对象提供唯一的实例。 3. 实现方式:懒汉单例类和饿汉单例类  3.1 懒汉式单例类...

    c#单例模式(Singleton)的6种实现

    在我们日常的工作中经常需要在应用程序中保持一个唯一的实例,如:IO处理,数据库操作等,由于这些对象都要占用重要的系统资源,所以我们必须限制这些实例的创建或始终使用一个公用的实例,这就是我们今天要介绍的...

    第9章_单例模式.ppt

    如何保证一个类只有一个实例并且这个实例易于被访问呢?定义一个全局变量可以确保对象随时都可以被访问,但不能防止我们实例化多个对象。 一个更好的解决办法是让类自身负责保存它的唯一...这就是单例模式的模式动机

    单例模式代码

    1、单例类只能有一个实例。 2、单例类必须自己创建自己的唯一实例。 3、单例类必须给所有其他对象提供这一实例。

    Objective C 单例模式设计源码

    顾名思义,单例模式的特点就是保证一个类仅有一个实例。因为这个模式只和一个类有关,没有类与类之间的关系,所有就不给出图示了。那么还是先说一下基本的定义。 单例模式(Singleton),保证一个类仅有一个实例,并...

    PHP单例模式详解及实例代码

    具体来说,作为对象的创建方式,单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统全局的提供这个实例。它不会创建实例副本,而是会向单例类内部存储的实例返回一个引用。 单例模式的特点 单例模式的...

    PHP单例模式是什么 php实现单例模式的方法

     作为对象的创建模式,单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统全局地提供这个实例。它不会创建实例副本,而是会向单例类内部存储的实例返回一个引用。 2、单例模式的三个要点: (1). 需要...

    单例模式Singleton(java源码)

    单例模式的特点有三: 单例类只能有一个实例。 单例类必须自己创建自己的唯一实例。 单例类必须给所有其他对象提供这一实例。 Singleton模式包含的角色只有一个,就是Singleton。Singleton拥有一个私有构造函数,...

    单例模式 饥汉模式和饱汉模式

    单例模式之饥汉模式和饱汉模式 通常我们可以让一个全局变量使得一个对象被访问,但它不能防止你实例化多个对象。 一个最好的方法就是,让类自身负责保存它的唯一实例, 这个类可以保护没有其他实例可以被创建,...

    Java+单例+多例+工厂+枚举+动态代理===》演示

    3. 定义一个静态方法返回这个唯一对象。 #### 例设计模式的类型 根据实例化对象的时机单例设计模式又分为以下两种: 1. 饿汉单例设计模式 2. 懒汉单例设计模式 多例设计模式案例演示 #### 多例设计模式的作用 多例...

    这可能是最全的单例模式了

    设计模式之——单例模式单例的几种实现1. 懒汉单例模式2. synchronized 修饰的懒汉单例模式3. 双重检查锁定的单例模式4. 静态内部类实现单例模式5. 饿汉实现单例模式6. 饿汉变种实现单例模式7. 枚举实现单例模式...

    java设计模式–单例模式推理过程

    单例类必须自己创建自己唯一的实例。 单例类必须给所有其他对象提供这个实例。 1.2 实现过程【懒汉】 第一次 /* * 1. 私有的构造方法 * 共有的话别人一调用就会有新的单例对象,没法保证单一的实例 * 2. 公有静态的...

    基于JavaScript实现单例模式

    首先,了解一下什么是单例模式,这里我直接把软件开发网中的定义给copy过来: 单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。...

    PHP设计模式之单例模式原理与实现方法分析

    作为对象的创建模式,单例模式确保某一个类只有一个实例,并且对外提供这个全局实例的访问入口。它不会创建实例副本,而是会向单例类内部存储的实例返回一个引用。 二、PHP单例模式三要素 1. 需要一个保存类的唯一...

    python单例模式原理与创建方法实例分析

    确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,单例模式是一种对象创建型模式。 2. 创建单例-保证只有1个对象 # 实例化一个单例 class Singleton(object): __instance = ...

    JavaScript设计模式之单例模式原理与用法实例分析

    单例模式典型的应用场景:单击按钮时,页面中会出现一个登陆浮窗,而该登录浮窗是唯一的,无论单击多少次按钮,这个浮窗都会被创建一次,则适合用单例模式创建。 全局变量不是单例模式,但在JavaScript开发中,经常...

    PHP单例模式详细介绍

    具体来说,作为对象的创建方式,单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统全局的提供这个实例。它不会创建实例副本,而是会向单例类内部存储的实例返回一个引用。 单例模式的特点 单例模式的...

    design-pattern-java.pdf

    工厂三兄弟之抽象工厂模式(二) 工厂三兄弟之抽象工厂模式(三) 工厂三兄弟之抽象工厂模式(四) 工厂三兄弟之抽象工厂模式(五) 单例模式-Singleton Pattern 确保对象的唯一性——单例模式 (一) 确保对象的...

Global site tag (gtag.js) - Google Analytics