这次分享我们就来谈谈单例模式的使用,其实在本公众号设计模式的第一篇分享就是单例模式,为什么又要讨论单例模式了?主要是那篇文章谈的比较浅,只对单例模式的主要思想做了一个分享,这篇文章会从多个方面去分享单例模式的使用,下面进入正题。
使用Java做程序的小伙伴都知道单例,尤其是使用spring框架做项目的,大家都知道spring框架管理类默认都是单例模式的,并且是线程安全的。那么如果保证一个类只被初始化一次且线程安全了?带着这个问题我们开始这篇文章。
在刚接触设计模式的时候,大多数人首先接触到的第一个设计模式就是单例模式,当然大多数人都只停留在单例模式,在平时的开发中用的少之又少,这篇文章会帮助大家从多个方面理解单例模式,此文代码较多,如果对单例模式概念不是很清楚的小伙伴可以看另一篇文章《设计模式之单例模式》。
1. 懒汉模式
package com.study.concurrency.base_concurrency.example.singleton;
/**
* <p>Title: Singleton01</p >
* <p>Description: 懒汉模式 单例实例在第一次使用时进行创建 </p >
* <p>Company: http://www.yinjiedu.com</p >
* <p>Project: concurrency</p >
*
* @author: qiwei
* @Date: 2019/8/20 22:41
* @Version: 1.0
*/
public class Singleton01 {
private Singleton01() {
}
//单例对象
private static Singleton01 instance = null;
/**
* @description: 静态的工厂方法
* @auther: qiwei
* @date: 2019/8/20 22:43
* @return: Singleton01
*/
public static Singleton01 getInstance() {
if (instance == null) {
instance = new Singleton01();
}
return instance;
}
}
上述代码在单线程下是没有问题的,但是在多线程情况下是线程不安全的,出现问题的代码主要是在下面这段代码:
if (instance == null) { instance = new Singleton01(); }
多个线程在同时获取instance的时候,会出现多个实例对象被new出来。
2. 饿汉模式
为了解决上面懒汉模式线程不安全问题,我们可以使用饿汉模式创建类实例。饿汉模式是单例实例在类装载时进行创建,代码如下:
package com.study.concurrency.base_concurrency.example.singleton; /** * <p>Title: Singleton02</p > * <p>Description: 饿汉模式</p > * <p>Company: http://www.yinjiedu.com</p > * <p>Project: concurrency</p > * * @author: qiwei * @Date: 2019/8/20 22:53 * @Version: 1.0 */ public class Singleton02 { private Singleton02() { } //单例对象 private static Singleton02 instance = new Singleton02(); /** * @description: 静态的工厂方法 * @auther: qiwei * @date: 2019/8/20 22:55 * @return: Singleton02 */ public static Singleton02 getInstance() { return instance; } }
饿汉模式问题:
饿汉模式虽然是线程安全的,但是在实际开发中,会有一些大家需要注意的问题。
第一点:饿汉模式是在类装载的时候就创建的,所以如果类的构造方法里面很多复杂的处理,类装载就会比较慢,影响程序性能;
第二点:饿汉模式创建的示例,不管在项目其他地方有没有使用,类的实例已经创建,如果不使用,会造成资源的浪费。
基于以上两点,大家在使用饿汉模式的时候需要注意实际的使用场景。
3. 懒汉模式之线程安全
我们看到在使用懒汉模式创建实例时是线程不安全的,这里我们使用同步锁的方式,让懒汉模式也是线程安全的,代码如下:
package com.study.concurrency.base_concurrency.example.singleton; /** * <p>Title: Singleton01</p > * <p>Description: 懒汉模式 </p > * <p>Company: http://www.yinjiedu.com</p > * <p>Project: concurrency</p > * * @author: qiwei * @Date: 2019/8/20 22:41 * @Version: 1.0 */ public class Singleton03 { private Singleton03() { } //单例对象 private static Singleton03 instance = null; /** * @description: 静态的工厂方法 * @auther: qiwei * @date: 2019/8/20 22:43 * @return: Singleton01 */ public static synchronized Singleton03 getInstance() { if (instance == null) { instance = new Singleton03(); } return instance; } }
这里我们使用了synchronized关键字保证了线程安全,但是在实际开发中不要这样写,因为synchronized会带来比较严重的性能开销。
4. 懒汉模式之双重同步锁模式(线程不安全)
使用双重同步锁实现单例模式,代码如下:
package com.study.concurrency.base_concurrency.example.singleton; /** * <p>Title: Singleton01</p > * <p>Description: 懒汉模式 </p > * <p>Company: http://www.yinjiedu.com</p > * <p>Project: concurrency</p > * * @author: qiwei * @Date: 2019/8/20 22:41 * @Version: 1.0 */ public class Singleton04 { private Singleton04() { } //单例对象 private static Singleton04 instance = null; /** * @description: 静态的工厂方法 * @auther: qiwei * @date: 2019/8/20 22:43 * @return: Singleton01 */ public static Singleton04 getInstance() { if (instance == null) { synchronized (Singleton04.class) { if (instance == null) { instance = new Singleton04(); } } } return instance; } }
上面代码在synchronized这里启用了双重检测机制,但是这种写法在多线程情况下也是线程不安全的,这个原因是JVM和CPU在的指令重排;这里简单的介绍一下什么是计算机指令:
Java实例化一个对象时,主要经历下面三步指令:
第一步:分配对象内存空间
第二步:初始化对象
第三步:设置instance指向刚分配的内存
知道了上面步骤,我们知道一旦在多线程情况下发生了指令重排,就会出现安全问题,例如上面的步骤123变成132 。
5. 懒汉模式之双重同步锁模式(线程安全)
使用第四种方式创建单例也是线程不安全的,要想使第四个例子线程安全,我们需要使用到一个关键字:volatile。
代码如下:
package com.study.concurrency.base_concurrency.example.singleton; /** * <p>Title: Singleton01</p > * <p>Description: 懒汉模式 </p > * <p>Company: http://www.yinjiedu.com</p > * <p>Project: concurrency</p > * * @author: qiwei * @Date: 2019/8/20 22:41 * @Version: 1.0 */ public class Singleton05 { private Singleton05() { } //单例对象 private volatile static Singleton05 instance = null; /** * @description: 静态的工厂方法 * @auther: qiwei * @date: 2019/8/20 22:43 * @return: Singleton01 */ public static Singleton05 getInstance() { if (instance == null) { synchronized (Singleton05.class) { if (instance == null) { instance = new Singleton05(); } } } return instance; } }
volatile可以禁止指令重排,所以上面方式是线程安全的。volatile使用场景主要是:状态标示量、双重检测。
6. 饿汉模式之静态域实现(错误写法)
饿汉模式第一种实现使用了直接初始化方式,这里我们可以使用静态域实现,这也是在平时开发中比较常见的一种方式,但是这里有一个坑,先埋给大家,代码如下:
package com.study.concurrency.base_concurrency.example.singleton; /** * <p>Title: Singleton02</p > * <p>Description: 饿汉模式</p > * <p>Company: http://www.yinjiedu.com</p > * <p>Project: concurrency</p > * * @author: qiwei * @Date: 2019/8/20 22:53 * @Version: 1.0 */ public class Singleton06 { private Singleton06() { } static { instance = new Singleton06(); } //单例对象 private static Singleton06 instance = null; /** * @description: 静态的工厂方法 * @auther: qiwei * @date: 2019/8/20 22:55 * @return: Singleton02 */ public static Singleton06 getInstance() { return instance; } }
上面静态域单例模式是有问题的,大家想想有什么问题,在最后我们再讨论,大家也可以debug调试找找问题。
7. 单例模式之枚举创建
在前面集中创建单例的例子中,我们接触了懒汉模式、饿汉模式。个人认为不管是懒汉模式还是饿汉模式,都不是最好的方案,这里我推荐使用枚举类创建单例,既保证了线程安全的需求,又保证了资源不被浪费。代码实现如下:
package com.study.concurrency.base_concurrency.example.singleton; /** * <p>Title: Singleton01</p > * <p>Description: 枚举模式 </p > * <p>Company: http://www.yinjiedu.com</p > * <p>Project: concurrency</p > * * @author: qiwei * @Date: 2019/8/20 22:41 * @Version: 1.0 */ public class Singleton07 { //私有构造函数 private Singleton07() { } //单例对象 private static Singleton07 instance = null; public static Singleton07 getInstance() { return Singleton.INSTANCE.getInstance(); } private enum Singleton { INSTANCE; private Singleton07 singleton; Singleton() { singleton = new Singleton07(); } public Singleton07 getInstance() { return singleton; } } }
上面代码,在使用枚举构造方法的时候JVM保证这个方法绝对只调用一次,个人推荐这种创建单例的模式。
解决6:饿汉模式之静态域实现问题
第六种饿汉模式创建单例的方式,主要是因为代码位置问题引起的,正确写法是把下面两段代码交换位置:
private static Singleton06 instance = null;
static { instance = new Singleton06(); }
这是在使用静态域时候特别容易犯的错误,希望大家注意。
关于单例模式的分享就到这里,不足之处希望大家提出来,我会及时修正。
获取实时信息,关注公众号:『编程之艺术』,二维码:
相关推荐
单例模式的核心在于控制类的实例化过程,常见的实现方式有懒汉式、饿汉式和双重检查锁定(DCL)等。懒汉式是在类被加载时并不创建实例,而是在第一次调用getInstance方法时才创建;饿汉式则是在类加载时就创建实例,...
线程安全的单例模式在多线程环境下尤其重要,因为不正确的实现可能导致多个线程创建多个实例,这违反了单例模式的基本原则。C++11引入了新的特性,如std::mutex和std::call_once,使得实现线程安全的单例模式变得...
单例模式是一种设计模式,它的主要目标是确保一个类只有一个实例,并提供一个全局访问点。在软件工程中,单例模式常用于控制资源的共享,比如数据库连接池...理解并正确使用单例模式对于构建高效、稳定的系统至关重要。
在提供的压缩包中,"logtest"可能是示例程序的名字,它展示了如何使用这个单例模式的日志类。通过运行这个示例,开发者可以了解如何配置日志等级、如何在多线程环境下安全地使用日志以及如何利用可变长参数和格式化...
接下来,我们将详细讨论单例模式的实现方式,包括懒汉式和饿汉式,以及如何通过同步枷锁来保证线程安全。 1. **懒汉式**(Lazy Initialization):懒汉式单例模式的特点是在第一次需要时才创建对象,即延迟初始化。...
单例模式有多种实现方式,常见的包括懒汉式、饿汉式以及双重检查锁定(DCL)等。 1. 懒汉式:懒汉式是在第一次使用时才创建单例对象,以延迟加载提高效率。但是,如果在多线程环境下,没有正确处理同步问题,可能...
单例模式是软件设计模式中的一种,它的核心思想是确保一个类在整个系统中只有一个实例,并提供一个...在实际开发中,我们需要根据具体需求选择合适的实现方式,同时注意线程安全问题,确保单例模式的正确性和稳定性。
为了正确地实现单例模式,需要注意以下几点: 1. **私有静态实例变量**:通常需要一个私有的静态变量来保存单例的实例。 ```php private static $_instance = null; ``` 2. **私有构造函数和克隆函数**:为了...
1. **线程安全的单例模式**:可以通过加锁或者使用内部类等方式来保证线程安全。例如,懒汉式单例模式中,可以在 `getInstance()` 方法上添加 `synchronized` 关键字,或者采用双重检查锁定(Double Checked Locking)...
本文将详细讨论四种常见的单例实现方式:饿汉模式、懒汉模式、双重检查锁定(DCL)单例模式以及枚举单例。 1. **饿汉模式**: 饿汉模式是在类加载时就完成了实例化,避免了线程同步问题。这种方式简单且安全,但...
单例模式的实现通常有以下几种方式: 1. **饿汉式(静态常量)**:这是最简单的单例实现方式,它在类加载时就完成了初始化,所以是线程安全的。代码如下: ```java public class Singleton { private static final...
在Qt的Qml环境中,单例模式是一种设计模式,它允许在整个应用程序中创建一个全局访问点,确保某个类只有一个实例存在。这样的设计模式在需要共享数据或者服务时非常有用,避免了多处创建相同对象导致的数据不一致或...
单例模式是软件设计模式中的一种,它的主要...在实际应用中,还需要考虑JVM的垃圾回收机制、序列化以及测试等方面的问题,以确保单例模式的正确性和健壮性。理解并熟练运用单例模式,有助于提高代码的可维护性和效率。
### 单例模式详解 #### 一、单例模式的概念 单例模式是软件开发中最基本的...正确理解和运用单例模式,可以有效提高软件的质量和效率。然而,也需要注意到单例模式的局限性和潜在的问题,在适当的情况下选择使用。
### 单例模式详解 #### 一、单例模式简介 单例模式(Singleton Pattern)是一种常用的软件设计模式,属于创建型模式之一。...在软件开发过程中,理解并正确使用单例模式对于构建高质量的应用程序至关重要。
在软件工程中,设计模式是解决常见问题的模板,它们是经过...理解并正确使用这些模式,可以帮助开发者更高效地组织和管理代码,从而构建出更高质量的软件系统。在实际项目中,根据具体需求选择合适的模式是至关重要的。
在软件设计模式中,工厂模式和单例模式是两种非常基础且重要的模式,它们都是用于解决对象创建问题,但有着不同的设计理念和应用场景。本篇文章将深入探讨这两种模式,并结合具体的代码示例`myFactoryDemo`进行讲解...