赞
踩
目录
第二种实现方式:懒汉模式,线程安全加锁synchronize
第四种实现方式:双重说锁/双重校验锁(DLC,即double-checked locking)
单例模式(Singleton Pattern)是Java中最常见最简单的设计模式之一。这种设计模式属于五个创建型的一种,他提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象方式,可以直接访问,不需要也不能直接从该类外部进行实例化,即在该类的外部不能直接使用new关键字进行实例化操作,而是通过该类提供的一个public的静态方法进行获取对象的实例。
注意:
一个类能返回对象的一个引用(永远是同一个对象的引用,地址相同)和一个获得该实例的方法(静态方法),当我们调用这个方法时,如果类持有的引用不为空就返回这个引用,如果类保持的引用为空就创建该类的实例并将实例的引用赋予该类保持的引用;同时还需要将该类的构造方法私有化(使用private修饰),这样其他处的代码就无法通调用该类的构造函数进行实例化(不能使用new关键字进行实例化)该类的对象,只可以使用该类对外提供的静态方法来获取该类的唯一实例。
实现单利模式的原则和过程:
单例类的构造器私有化,保证一个类有且仅有一个实例,并提供一个访问它的全局访问点,即提供内部实例化的静态方法。
判断系统是否有单例类的实例,若有则返回实例,没有每部实例化;消除一个全局使用的类频繁地创建与销毁,而影响系统的性能;单例模式可以控制实例数目,节省系统资源的时候。
适用场景:
单例模式只允许创建一个对象,因此节省内存,加快对象访问速度,因此对象需要被公用的场合适合使用,如多个模块使用同一个数据源连接对象等等。如:
1.需要频繁实例化然后销毁的对象。
2.创建对象时耗时过多或者耗资源过多,但又经常用到的对象。
3.有状态的工具类对象。
4.频繁访问数据库或文件的对象。
优点:
缺点:
单例模式在多线程情况下,使用必须小心。如果当唯一实例尚未被创建,有两个线程同时调用创建噶单例类的实例时,那么他们同时没有检测到唯一实例的存在,从而同时各自创建一个实例(对于单个线程而言,创建一个实例时符合单例模式特点的),这就可能会创建两个实例,从而违反了单例模式中实例唯一性的原则。解决这个问题的办法就是为目标类是否已经实例提供一个互斥锁(当然这样会降低效率)。
1、使用时不能用反射模式创建单例,否则会实例化一个新的对象
2、使用懒单例模式时注意线程安全问题
3、饿单例模式和懒单例模式构造方法都是私有的,因而是不能被继承的,有些单例模式可以被继承(如登记式模式)
第一步:创建一个Singleton类
-
- /**
- * Copyright © 2018 GBOYS CSDN Blog Module. All rights reserved.
- *
- * @Package: com.gxboys.pattern.singleton
- */
- package com.gxboys.pattern.singleton;
-
- /**
- * @See: Using GXBOYS Blog In CSDN
- *
- * @ClassName: Singleton
- * @Description: 单例模式(流程测试)
- * @author: GXBOYS
- * @date: 2018年5月5日 下午6:29:31
- */
- public class Singleton {
-
- // 创建单例对象
- private static Singleton instance = new Singleton();
-
- // 私有化构造器,外部无法通过new关键字实例化该对象
- private Singleton() {
- }
-
- // 对外提供一个唯一可以获取对象接口方法
- public static Singleton getInstance() {
- return instance;
- }
-
- // 测试方法
- public void showMessage() {
- System.out.println("欢迎学习单例模式流程.");
- }
- }
第二步:从Singleton外部获取对象(Test类调用)
- package com.gxboys.pattern.singleton;
-
- public class Test {
-
- public static void main(String[] args) {
-
- Singleton singleton = null;
-
- // 使用new关键字 编译时会报错,提示:The constructor Singleton() is not visible.
- // singleton = new Singleton();
-
- // 获取唯一可用的对象
- singleton = Singleton.getInstance();
-
- // 显示消息
- singleton.showMessage();
- }
- }
第三步:测试结果
1、使用关键字new获取对象
2、调用方法获取对象
单例模式实现方式一般分为懒汉模式、饿汉模式
代码实例
- /**
- * Copyright © 2018 GBOYS CSDN Blog Module. All rights reserved.
- *
- * @Package: com.gxboys.pattern.singleton
- */
- package com.gxboys.pattern.singleton;
-
- /**
- * @See: Using GXBOYS Blog In CSDN
- *
- * @ClassName: LazySingleton
- * @Description: 懒汉模式,线程不安全
- * @author: GXBOYS
- * @date: 2018年5月5日 下午7:10:38
- */
- public class LazySingleton {
- // 设置实例变量
- private static LazySingleton instance;
-
- // 私有化构造器
- private LazySingleton() {
- }
-
- // 对外提供获取对象实例接口
- public static LazySingleton getInstance() {
- // 懒汉模式,对象实例存在直接返回,不存在则内部实例化
- if (instance == null) {
- instance = new LazySingleton();
- }
- return instance;
- }
- }
代码实例
- /**
- * Copyright © 2018 GBOYS CSDN Blog Module. All rights reserved.
- *
- * @Package: com.gxboys.pattern.singleton
- */
- package com.gxboys.pattern.singleton;
-
- /**
- * @See: Using GXBOYS Blog In CSDN
- *
- * @ClassName: LazySingleton
- * @Description: 懒汉模式,线程安全
- * @author: GXBOYS
- * @date: 2018年5月5日 下午7:26:35
- */
- public class LazySingleton {
-
- private static LazySingleton instance;
-
- private LazySingleton() {
- }
-
- // 加锁
- public static synchronized LazySingleton getInstance() {
- if (instance == null) {
- instance= new LazySingleton();
- }
- return instance;
- }
- }
它基于 classloder 机制避免了多线程的同步问题,不过,instance 在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用 getInstance 方法, 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化 instance 显然没有达到 lazy loading 的效果。
代码实例
- /**
- * Copyright © 2018 GBOYS CSDN Blog Module. All rights reserved.
- *
- * @Package: com.gxboys.pattern.singleton
- */
- package com.gxboys.pattern.singleton;
-
- /**
- * @See: Using GXBOYS Blog In CSDN
- *
- * @ClassName: HungarySingleton
- * @Description: 恶汉模式
- * @author: GXBOYS
- * @date: 2018年5月5日 下午7:40:46
- */
- public class HungarySingleton {
-
- private static HungarySingleton instance = new HungarySingleton();
-
- private HungarySingleton() {
- }
-
- public static HungarySingleton getInstance() {
- return instance;
- }
- }
getInstance() 的性能对应用程序很关键。
代码实例
- /**
- * Copyright © 2018 GBOYS CSDN Blog Module. All rights reserved.
- *
- * @Package: com.gxboys.pattern.singleton
- */
- package com.gxboys.pattern.singleton;
-
- /**
- * @See: Using GXBOYS Blog In CSDN
- *
- * @ClassName: DLCSingleton
- * @Description: 双重说锁/双重校验锁模式
- * @author: GXBOYS
- * @date: 2018年5月5日 下午7:53:42
- */
- public class DLCSingleton {
-
- private volatile static DLCSingleton instance;
-
- private DLCSingleton() {
- }
-
- public static DLCSingleton getSingleton() {
- if (instance== null) {
- synchronized (DLCSingleton.class) {
- if (instance== null) {
- instance= new DLCSingleton();
- }
- }
- }
- return instance;
- }
- }
注: DLC模式需要借助关键字volatile,了解这个关键字需要什么研究JVM相关的重排序特性,在这儿暂不作介绍,后续会单独讲解DLC模式。这个同时需要JDK5或更高版本(因为从JDK5开始使用新的JSR-133内存模型规范,这个规范增强了volatile的语义)
这种方式只适静态域的情况,DLC方式可以使用于实例域需要加载时使用。该方式同样利用classLoader机制来保证初始化instance时只有一个线程,它跟第三种方式不同的是:第三种只要Singleton类被装载了,那么instance就会被实例化(没有达到lazy loading的效果),而这种方式是Single类被加载了,instance不一定被初始化。因为SingletonHoller类没有被主动使用,只有通过显示调用getInstance方法时,才会显显式装载SingletonHolder类,从而实例化instance。想象一下如果实例化 instance 很消耗资源,所以想让它延迟加载,另外一方面,又不希望在 Singleton 类加载时就实例化,因为不能确保 Singleton 类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化 instance 显然是不合适的。这个时候,这种方式相比第 3 种方式就显得很合理。
代码实例
- /**
- * Copyright © 2018 GBOYS CSDN Blog Module. All rights reserved.
- *
- * @Package: com.gxboys.pattern.singleton
- */
- package com.gxboys.pattern.singleton;
-
- /**
- * @See: Using GXBOYS Blog In CSDN
- *
- * @ClassName: StaticSingleton
- * @Description: TODO
- * @author: 登记式/静态内部类
- * @date: 2018年5月5日 下午9:46:48
- */
- public class StaticSingleton {
-
- private static class SingletonHolder {
- private static final StaticSingleton INSTANCE = new StaticSingleton();
- }
-
- private StaticSingleton() {
- }
-
- public static final StaticSingleton getInstance() {
- return SingletonHolder.INSTANCE;
- }
- }
一般情况下的赘述:不建议使用第 1 种和第 2 种懒汉方式,建议使用第 3 种饿汉方式。只有在要明确实现 lazy loading 效果时,才会使用第 5 种登记方式。如果涉及到反序列化创建对象时,可以尝试使用枚举方式。如果有其他特殊的需求,可以考虑使用第 4 种双检锁方式。
--------------------------------------------------------------------------------------
作者:超级字节码
来源:CSDN
原文:https://blog.csdn.net/dgxin_605/article/details/80205734
版权声明:本文为博主原创文章,转载请附上博文链接!
--------------------------------------------------------------------------------------
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。