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等。
相关推荐
单例模式的应用场景包括:控制资源的访问、管理复杂的初始化过程以及在系统中创建一个全局的配置对象等。 单例模式分为两种主要实现方式:饿汉模式和懒汉模式。 1. 饿汉模式(Eager Initialization): 饿汉模式的...
对于C++,由于其不支持全局作用域的对象,因此实现单例模式需要更复杂的技巧。一种常见的C++单例模式实现方式是使用静态成员变量和私有构造函数: ```cpp #include class Singleton { private: Singleton() {} /...
总之,单例模式是iOS开发中常见的设计模式之一,它能够提供一种优雅的方式来管理和访问全局唯一的对象实例。无论是通过传统的非ARC方法,还是现代的ARC+GCD方式,实现单例模式都需要仔细考虑其线程安全性和生命周期...
当需要一个类在整个程序生命周期内只存在一个实例,且这个实例需要被多个对象共享时,可以采用单例模式。例如,网络请求管理、用户偏好设置、应用配置信息的存储等场景,都适合使用单例模式。 1.1. iOS 中单例模式...
在软件设计领域,设计模式是一种被广泛接受的解决常见问题的最佳实践。C++中的设计模式是面向...在实际开发中,结合VC环境的特性,工厂模式和单例模式能帮助我们更好地管理对象的创建和生命周期,提高软件的可靠性。
单例模式是面向对象编程中一个非常重要的设计模式,它保证了一个类只有一个实例,并提供一个全局访问点。这种模式在Java中广泛应用于配置管理、线程池、缓存管理等场景,因为它能确保系统资源的有效利用,防止无限制...
单例模式”和“创建模式-2.简单工厂模式”这两个文件,你将能看到如何在实际项目中应用这些模式,从而加深理解和实践。在阅读源代码的过程中,注意观察如何通过类的组织和方法的设计来实现这些模式的特性。这将有助...
工厂模式用于解耦对象的创建和使用,而单例模式则用于控制对象的唯一性。通过深入理解和实践这些模式,开发者可以编写出更加优雅、易于维护的代码。在阅读和分析提供的`DesignMode`代码示例时,你会更好地掌握这两种...
在单例模式中,类的构造函数是私有的,防止外部直接创建对象,而是通过静态方法获取该类的唯一实例。单例模式的唯一性通常是在进程范围内,即在同一个进程中,无论何时调用单例类的获取实例方法,都会返回相同的对象...
单例模式是软件设计模式中的一种经典模式,它在Java编程中被广泛使用。这个模式的主要目的是确保一个类只有一个实例,并提供一个全局访问点。这样做的好处在于可以控制实例的数量,减少资源消耗,同时便于协调整个...
- 懒汉式(Lazy Initialization):在第一次调用getInstance时才创建单例对象。如果不使用单例,就不会创建,节省资源。但线程不安全,需要通过同步机制保证。 ```java public class Singleton { private static ...
ava常用设计模式-单例模式 单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一,有以下特点: 1. 单例类只能有一个实例。 2. 单例类必须自己创建自己的唯一实例。 3. 单例类必须给所有其他对象提供这一...
Java设计模式-单例模式详解 单例模式是 Java 设计模式中的一种常用的设计模式,旨在保证一个类仅有一个实例,并提供一个访问它的全局访问点。单例模式的目的是为了保证在一个进程中,某个类有且仅有一个实例。 ...
1. **系统开销**:虽然单例模式可以减少不必要的对象创建,但在某些情况下,过度依赖单例可能会增加系统的复杂性和内存占用,尤其是在大型项目中。 2. **开发混淆**:由于隐藏了实例化的细节,可能导致代码的可读性...
在JavaScript中,由于语言的特性,实现单例模式有多种方式,但目标都是限制构造函数的直接调用,同时提供一个公共的访问点获取该唯一实例。 **经典实现** 一种经典的JavaScript单例模式实现是利用闭包来控制实例化...
这样做的好处是隐藏了对象创建的复杂性,客户端只需要知道如何获取产品,而不需要了解产品是如何被创建的。工厂模式分为简单工厂模式、工厂方法模式和抽象工厂模式,根据具体需求选择合适的实现。 在这个项目中,...
4. **实现单例方法**:在`+ (instancetype)sharedInstance`方法中,第一次调用时创建单例,之后则返回已创建的单例。 ```objc + (instancetype)sharedInstance { static dispatch_once_t onceToken; dispatch_...
总之,JavaScript中的单例模式是一种强大的设计模式,能够帮助我们有效地控制对象的生命周期,优化资源管理和提升代码质量。在阅读和理解提供的`js代码-单例单例单例单例`压缩包时,重点应该关注单例模式的实现细节...
3. **控制共享状态**:当对象的状态需要在应用程序的各个部分之间共享时,单例模式能提供一个全局访问点来管理这些状态。 **缺点**: 1. **测试困难**:因为单例模式的全局访问点,单元测试变得复杂,因为它违反了...