单例模式是一种对象创建模式,它用于产生一个对象的具体实例,它可以确保系统中一个类只产生一个实例。Java 里面实现的单例是一个虚拟机的范围,因为装载类的功能是虚拟机的,所以一个虚拟机在通过自己的 ClassLoad 装载实现单例类的时候就会创建一个类的实例。在 Java 语言中,这样的行为能带来两大好处:
-
对于频繁使用的对象,可以省略创建对象所花费的时间,这对于那些重量级对象而言,是非常可观的一笔系统开销;
-
由于 new 操作的次数减少,因而对系统内存的使用频率也会降低,这将减轻 GC 压力,缩短 GC 停顿时间。
1.饿汉式
/** * 饿汉式 * @author BaShen */ public class Singleton { private User user; private Singleton() { user = new User(); } private static final Singleton instance = new Singleton(); public static Singleton getInstance() { return instance; } public User getUser() { return user; } }
2.懒汉式
/** * 缺陷的懒汉式 * @author BaShen * */ public class SingletonOne { private SingletonOne() { } private static SingletonOne instance = null; public static SingletonOne getInstance() { if (instance == null) { synchronized (SingletonOne.class) { if (instance == null) { instance = new SingletonOne(); } } } return instance; } }
/** * 下面我们开始说编译原理。所谓编译,就是把源代码“翻译”成目标代码——大多数是指机器代码——的过程。 * 针对Java,它的目标代码不是本地机器代码,而是虚拟机代码。 * 编译原理里面有一个很重要的内容是编译器优化。 * 所谓编译器优化是指,在不改变原来语义的情况下,通过调整语句顺序,来让程序运行的更快。 * 这个过程成为reorder(重排)。 * 要知道,JVM只是一个标准,并不是实现。JVM中并没有规定有关编译器优化的内容,也就是说,JVM实现可以自由的进行编译器优化。 * 下面来想一下,创建一个变量需要哪些步骤呢? * 一个是申请一块内存,调用构造方法进行初始化操作,另一个是分配一个指针指向这块内存。这两个操作谁在前谁在后呢? * JVM规范并没有规定。那么就存在这么一种情况,JVM是先开辟出一块内存,然后把指针指向这块内存,最后调用构造方法进行初始化。 * 下面我们来考虑这么一种情况:线程A开始创建SingletonClass的实例,此时线程B调用了getInstance()方法,首先判断instance是否为null。 * 按照我们上面所说的内存模型,A已经把instance指向了那块内存,只是还没有调用构造方法,因此B检测到instance不为null, * 于是直接把instance返回了——问题出现了,尽管instance不为null,但它并没有构造完成,就像一套房子已经给了你钥匙, * 但你并不能住进去,因为里面还没有收拾。此时,如果B在A将instance构造完成之前就是用了这个实例,程序就会出现错误了! * 于是,我们想到了下面的代码: */
public class SingletonOne { private SingletonOne() { } private static SingletonOne instance = null; public static SingletonOne getInstance() { if (instance == null) { SingletonOne sc= null; synchronized (SingletonOne.class) { sc= instance; if(slo == null){ synchronized (SingletonOne.class) { if (sc == null) { sc= new SingletonOne(); } } instance = sc; } } } return instance; } }
/** *我们在第一个同步块里面创建一个临时变量,然后使用这个临时变量进行对象的创建,并且在最后把instance指针临时变量的内存空间。 *写出这种代码基于以下思想,即synchronized会起到一个代码屏蔽的作用,同步块里面的代码和外部的代码没有联系。 *因此,在外部的同步块里面对临时变量sc进行操作并不影响instance,所以外部类在instance=sc;之前检测instance的时候,结果instance依然是null。 *不过,这种想法完全是错误的! *同步块的释放保证在此之前——也就是同步块里面——的操作必须完成,但是并不保证同步块之后的操作不能因编译器优化而调换到同步块结束之前进行。 *因此,编译器完全可以把instance=sc;这句移到内部同步块里面执行。这样,程序又是错误的了! */
/** * 说了这么多,难道单例没有办法在Java中实现吗?其实不然! * 在JDK 5之后,Java使用了新的内存模型。volatile关键字有了明确的语义——在JDK1.5之前, * volatile是个关键字,但是并没有明确的规定其用途——被volatile修饰的写变量不能和之前的读写代码调整, * 读变量不能和之后的读写代码调整!因此,只要我们简单的把instance加上volatile关键字就可以了。 */
完整的懒汉式
/** * 完整的懒汉式 * @author BaShen */ public class SingletonOne { private SingletonOne() { } private volatile static SingletonOne instance = null; public static SingletonOne getInstance() { if (instance == null) { synchronized (SingletonOne.class) { if(instance == null){ instance = new SingletonOne(); } } } return instance; } }
完整的懒汉式
/** * 完整的懒汉式 * @author BaShen */ public class SingletonFive { private SingletonFive() { init(); } private void init() { user = new User(); } private User user; private static class LazyHolder { private static final SingletonFive INSTANCE = new SingletonFive(); } public static SingletonFive getInstance(){ return LazyHolder.INSTANCE; } public User getUser(){ return user; } }
3.枚举单例
/** * 枚举 推荐的单例 * @author BaShen */ public enum SingletonEnum { INSTANCE; private SingletonEnum() { user = new User(); } private User user = null; public User getUser() { return user; } }
4.登记式单例模式
/** * * @author BaShen */ public class SingletonRegistryManager { private static Map<String, Object> singletonMap = new HashMap<>(); /** * 获取实例 * @param className * @return */ public static Object getInstance(String className) { if (isRegistred(className)) { return singletonMap.get(className); } return null; } /** * 获取实例 * @param clazz * @return */ public static Object getInstance(Class clazz) { return getInstance(clazz.getName()); } /** * 注册 * @param className */ public synchronized static void registry(String className) { if (!singletonMap.containsKey(className)) { try { singletonMap.put(className, Class.forName(className).newInstance()); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } } /** * 注册 * @param clazz */ public synchronized static void registry(Class clazz) { registry(clazz.getName()); } /** * 反注册 * @param className */ public synchronized static void unRegistry(String className) { if (singletonMap.containsKey(className)) { singletonMap.remove(className); } } /** * 反注册 * @param clazz */ public synchronized static void unRegistry(Class clazz) { unRegistry(clazz.getName()); } /** * 判断是否注册 * @param className * @return */ public synchronized static boolean isRegistred(String className) { return singletonMap.containsKey(className); } public synchronized static boolean isRegistred(Class clazz) { return isRegistred(clazz.getName()); } }
相关推荐
单例设计模式是一种在软件工程中广泛使用的创建型设计模式,其核心思想是确保一个类仅有一个实例,并提供一个全局访问点。这种模式在多种场景下具有显著的优势,同时也存在一定的局限性和潜在的问题。 ### 单例设计...
单例设计模式是一种常用的设计模式,它保证一个类只有一个实例,并提供一个全局访问点。在Java编程中,单例模式的应用广泛,例如控制资源的共享、管理配置信息等。本篇将深入解析单例设计模式的四种实现方式,并通过...
单例设计模式是软件设计模式中的经典模式之一,它的核心思想是确保一个类只有一个实例,并提供全局访问点。这种模式在Java中广泛应用于系统配置、线程池、缓存管理等场景,因为这些场景通常只需要一个共享的全局对象...
单例设计模式是一种在软件设计中广泛使用的设计模式,它属于创建型模式,其核心目标是确保类只有一个实例,并提供一个全局访问点。这个模式在Java中尤其常见,因为Java的垃圾回收机制允许对象长时间驻留在内存中,...
单例设计模式是软件开发中一种重要的设计模式,它的核心思想是确保一个类只有一个实例,并提供全局访问点。在Java中,单例模式通常用于控制特定类的实例化过程,以达到节省系统资源、控制并发访问和实现数据共享等...
C++单例设计模式,单例模式 C++单例设计模式,单例模式
单例设计模式是一种在软件工程中广泛使用的设计模式,它的主要目标是确保一个类只有一个实例,并提供一个全局访问点。这种模式在系统中需要频繁创建和销毁对象,且对象需要跨模块共享时特别有用,例如数据库连接、...
单例设计模式是一种重要的软件设计模式,用于确保一个类只有一个实例,并提供全局访问点。这种模式在C++中尤其常见,因为它允许多个组件共享同一对象,从而提高效率和一致性。以下是对C++单例模式的详细说明: 1. *...
在Qt框架中,单例设计模式是一种经常被用到的设计模式,它确保一个类只有一个实例,并提供全局访问点。这个模式在管理共享资源、配置文件或者数据库连接等方面特别有用。本系列教程将深入探讨Qt中的单例设计模式,...
单例设计模式、工厂设计模式和抽象工厂模式是常见的软件开发设计模式。这些设计模式提供了一些有用的思想和实现方式,可以帮助开发人员在设计和实现复杂的软件系统时,更加灵活和高效地进行编程。 单例设计模式是一...
单例设计模式Singleton1 单例设计模式Singleton1是Java设计模式中的一种创建型模式,指某个类采用Singleton模式,则在这个类被创建后,只可能产生一个实例供外部访问,并且提供一个全局的访问点。这个模式的核心...
java单例设计模式
单例设计模式是一种在软件设计中广泛使用的设计模式,它保证了一个类只有一个实例,并提供一个全局访问点。这种模式在需要频繁创建和销毁对象,或者对象的创建代价高昂时特别有用,例如数据库连接、线程池等。在C#中...
Java中懒汉单例设计模式线程安全测试,单例设计模式的测试
单例模式(单例设计模式)详解1
Java中的单例设计模式是一种创建型设计模式,用于确保一个类只有一个实例,并提供一个全局访问点。这种模式常用于管理共享资源,如数据库连接、线程池或配置对象。单例模式的关键在于限制类的实例化,防止多个实例的...
PHP单例设计模式,连接多数据库源码 单例模式的三个特点 1: 一个类只能有一个实例 2: 它保修自行创建这个实例 3: 必须自行向整个系统提供这个实例. 单例模式中主要的角色 Singleton定义一个Instance操作,允许客户...
### 设计模式—单例设计模式详解 #### 定义 单例模式是一种常见的创建型设计模式,其核心思想在于确保一个类仅有一个实例存在,并且该实例由该类自行创建,随后向整个系统提供这一唯一实例。 #### 类图解析 在设计...