`
iamzhongyong
  • 浏览: 806339 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

关于单例模式(代码篇)

阅读更多

很早的时候,转发过一篇单例模式的文章:http://iamzhongyong.iteye.com/blog/1539642 

最近又翻了一本设计模式的书,然后发现单例其实也简单也复杂,于是就打算把代码敲一下,保存下来。

------------------------------------------------------------------------------------------------------

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package singleton;
/**
 * 最简单的单例模式
 */
public class SimpleSingleton {
 
    /**
     * 构造方法私有化,外部无法通过构造方法创建对象,这样能够屏蔽外部直接new
     * 还有就是反射了,反射时可以使用setAccessible方法来突破private的限制,
     * 我们需要做到第一点工作的同时,还需要在在ReflectPermission("suppressAccessChecks")
     * 权限下使用安全管理器(SecurityManager)的checkPermission方法来限制这种突破,
     * 一般来说,不会真的去做这些事情,都是通过应用服务器进行后台配置实现。
     * 再就是序列化了,序列化会在SimpleSerializableSingleton这个类中做介绍
     */
    private SimpleSingleton(){}
 
    /**
     * 类型是static,这样在JVM进行类加载的时候就会做类的实例化,JVM保证线程安全
     * 根据JLS(Java Language Specification)中的规定,一个类在一个ClassLoader中只会被初始化一次,
     * 这点是JVM本身保证的,那就把初始化实例的事情扔给JVM好了
     */
    private static final SimpleSingleton instance = new SimpleSingleton();
 
    //通过一个静态方法,获得这个对象
    public static SimpleSingleton getInstance(){
        return instance;
    }
}

 

 

 

------------------------------------------------------------------------------------------------------

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
package singleton;
/**
 * 单例模式的懒加载策略,不在类加载的时候进行实例化,而是在第一次调用的时候进行
 */
public class SimpleLazySingleton {
 
    //私有构造方法
    private SimpleLazySingleton(){}
 
    //在类加载的时候,这个对象不进行实例化,volatile变量,拥有可见性
    private static volatile SimpleLazySingleton instance = null;
 
    /**
     * @deprecated
     * 这种会有线程安全问题,因为可能存在多线程访问这个方法,这个时候对象就有可能不是单例的
     */
    public static SimpleLazySingleton getInstanceNotSafe(){
        if(instance == null){
            instance = new SimpleLazySingleton();
        }
        return instance;
    }
 
    /**
     * @deprecated
     * 做一个简单的处理,就是在getInstance的时候添加锁关键字
     * 但是这样有个问题,就是所有的getInstance操作全部加锁,性能会下降很多
     */
    public static synchronized SimpleLazySingleton getInstanceSyncSafe(){
        if(instance == null){
            instance = new SimpleLazySingleton();
        }
        return instance;
    }
 
    /**
     * @deprecated
     * 那就做锁的细化吧,把锁的处理挪到方法体内部,仅仅在instance为空的时候,再去加锁
     */
    public static SimpleLazySingleton getInstanceSyncNotSafe(){
        if(instance == null){
            synchronized (SimpleLazySingleton.class) {
                instance = new SimpleLazySingleton();
            }
        }
        return instance;
    }
 
    /**
     * 那就做锁的细化吧,把锁的处理挪到方法体内部,仅仅在instance为空的时候,再去加锁
     */
    public static SimpleLazySingleton getInstance(){
        if(instance == null){
            synchronized (SimpleLazySingleton.class) {
                /**
                 * 这里称之为double-check-lock,为啥要做这不操作呢?
                 * 因为可能有多个线程进入第一个“if(instance == null)”,这个时候,线程去强占锁,
                 * 抢到锁的线程进行instance的初始化操作,完了之后释放锁,
                 * 第二个线程获得锁,这个时候进入之后,如果没有判空操作,会再一次初始化了实例,这时候就不是单例了
                 */
                if(instance == null){
                    instance = new SimpleLazySingleton();
                }
            }
        }
        return instance;
    }
}

------------------------------------------------------------------------------------------------------

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package singleton;
 
/**
 * 通过Holder的形式来进行,利用JVM的机制来保障线程安全
 */
public class SimpleHolderSingleton {
 
    //私有化
    private SimpleHolderSingleton(){}
 
    //类中有一个私有的XXXHolder类,这个因为是static类型的,所以在JVM加载类的时候就会加载到,但是INSTANCE就不会
    private static class SimpleHolderSingletonHolder{
        //持有外部类的属性
        static final SimpleHolderSingleton INSTANCE = new SimpleHolderSingleton();
    }
 
    //这样会在第一次调用的时候进行初始化操作,因为INSTANCE是static的,所以借助了JVM的机制来保障线程安全
    public static SimpleHolderSingleton getInstance(){
        return SimpleHolderSingletonHolder.INSTANCE;
    }
}

 

 

------------------------------------------------------------------------------------------------------

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package singleton;
 
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
 
/**
 * 如果单例的类实现了序列化接口,这个时候需要做一下特殊处理,
 */
public class SimpleSerializableSingleton implements java.io.Serializable{
 
    private static final long serialVersionUID = -589503673156379879L;
 
    //屏蔽外部new的实例化
    private SimpleSerializableSingleton(){}
 
    private static SimpleSerializableSingleton instance = new SimpleSerializableSingleton();
 
    public static SimpleSerializableSingleton getInstance(){
        return instance;
    }
 
    /**
     * 这个方法,会在发序列化构建对象的时候调用到,如果不这么处理
     * 反序列化之后的对象,是另外一个内存地址,也就是说不再是单例的了
     */
    private Object readResolve() {
        System.out.println("readResolve,被调用了");
        return getInstance(); 
    
 
    public static void main(String[] args) throws Exception {
        SimpleSerializableSingleton simple = SimpleSerializableSingleton.getInstance();
        //获得单例对象的内存地址
        System.out.println(simple);
        //定义序列化写入的文件
        File file = new File("d:\\git\\serializable");
        //构造objectOutputStream
        ObjectOutputStream outStream = new ObjectOutputStream(new FileOutputStream(file));
        //写入对象
        outStream.writeObject(simple);
        outStream.close();
 
        //反序列化
        ObjectInputStream inStream = new ObjectInputStream(new FileInputStream(file));
        SimpleSerializableSingleton simpeFromSeria = (SimpleSerializableSingleton)inStream.readObject();
        System.out.println(simpeFromSeria);
        inStream.close();
    }
 
}

------------------------------------------------------------------------------------------------------

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package singleton;
 
import java.lang.reflect.ReflectPermission;
import java.security.Permission;
/**
 * 如何禁止外部通过反射来做单例对象的序列化
 */
public class SimpleReflectionSingleton {
     
    private SimpleReflectionSingleton(){}
     
    private static SimpleReflectionSingleton instance = new SimpleReflectionSingleton();
     
    public static SimpleReflectionSingleton getInstance(){
        return instance;
    }
    public static void main(String[] args) throws Exception{
         
        //启动JVM的安全检察,在进行反射校验的时候,判断一下是否是“singleton”,如果是,就禁止反射
        System.setSecurityManager(new SecurityManager(){
            @Override
            public void checkPermission(Permission perm) {
                if (perm instanceof ReflectPermission && "suppressAccessChecks".equals(perm.getName())) {
                     for (StackTraceElement elem : Thread.currentThread().getStackTrace()) {
                          if (elem.getClassName().endsWith("Singleton")) {
                              throw new SecurityException();
                          }
                     }
                 }
            }
        });
         
        SimpleReflectionSingleton simple = SimpleReflectionSingleton.getInstance();
        System.out.println(simple);
         
        Class<?> clazz = SimpleReflectionSingleton.class;
         
        SimpleReflectionSingleton ref = (SimpleReflectionSingleton)clazz.newInstance();
         
        System.out.println(ref);
    }
}

3
2
分享到:
评论
4 楼 851228082 2015-04-22  
这是我见过最全面的介绍单例模式的code了。赞!
3 楼 在世界的中心呼喚愛 2014-04-24  
iamzhongyong 写道
last_forever 写道
这个
//类中有一个私有的XXXHolder类,这个因为是static类型的,所以在JVM加载类的时候就会加载到,但是INSTANCE就不会
    private static class SimpleHolderSingletonHolder{
        //持有外部类的属性
        static final SimpleHolderSingleton INSTANCE = new SimpleHolderSingleton();
    }

与这个
private static final SimpleSingleton instance = new SimpleSingleton();


有什么区别呢

第二个是在加载这个类的时候就初始化了。
第一个的话,SimpleHolderSingletonHolder因为是static的,所以在加载的时候会加载进来,但是里面的属性,会在用到的时候才加载,是一种懒加载的模式,通过JVM加载类的机制来保证线程安全的


static 和final会直接读取变量或者对象(我记得随机数不行)
static只是加载class类,需要的时候才会初始化class
2 楼 iamzhongyong 2014-04-23  
last_forever 写道
这个
//类中有一个私有的XXXHolder类,这个因为是static类型的,所以在JVM加载类的时候就会加载到,但是INSTANCE就不会
    private static class SimpleHolderSingletonHolder{
        //持有外部类的属性
        static final SimpleHolderSingleton INSTANCE = new SimpleHolderSingleton();
    }

与这个
private static final SimpleSingleton instance = new SimpleSingleton();


有什么区别呢

第二个是在加载这个类的时候就初始化了。
第一个的话,SimpleHolderSingletonHolder因为是static的,所以在加载的时候会加载进来,但是里面的属性,会在用到的时候才加载,是一种懒加载的模式,通过JVM加载类的机制来保证线程安全的
1 楼 last_forever 2014-04-23  
这个
//类中有一个私有的XXXHolder类,这个因为是static类型的,所以在JVM加载类的时候就会加载到,但是INSTANCE就不会
    private static class SimpleHolderSingletonHolder{
        //持有外部类的属性
        static final SimpleHolderSingleton INSTANCE = new SimpleHolderSingleton();
    }

与这个
private static final SimpleSingleton instance = new SimpleSingleton();


有什么区别呢

相关推荐

    Python项目-自动办公-59 PPT_pptx_在PPT中写入图片和表格.zip

    Python课程设计,含有代码注释,新手也可看懂。毕业设计、期末大作业、课程设计、高分必看,下载下来,简单部署,就可以使用。 包含:项目源码、数据库脚本、软件工具等,该项目可以作为毕设、课程设计使用,前后端代码都在里面。 该系统功能完善、界面美观、操作简单、功能齐全、管理便捷,具有很高的实际应用价值。

    Python项目-实例-20 快递查询.zip

    Python课程设计,含有代码注释,新手也可看懂。毕业设计、期末大作业、课程设计、高分必看,下载下来,简单部署,就可以使用。 包含:项目源码、数据库脚本、软件工具等,该项目可以作为毕设、课程设计使用,前后端代码都在里面。 该系统功能完善、界面美观、操作简单、功能齐全、管理便捷,具有很高的实际应用价值。

    杂货产品检测43-YOLO(v5至v9)、CreateML、Paligemma、TFRecord、VOC数据集合集.rar

    杂货产品检测43-YOLO(v5至v9)、CreateML、Paligemma、TFRecord、VOC数据集合集.rarIPCV分配-V6 2024-01-21 6:10 PM ============================= *与您的团队在计算机视觉项目上合作 *收集和组织图像 *了解和搜索非结构化图像数据 *注释,创建数据集 *导出,训练和部署计算机视觉模型 *使用主动学习随着时间的推移改善数据集 对于最先进的计算机视觉培训笔记本,您可以与此数据集一起使用 该数据集包括7012张图像。 家庭废物以createMl格式注释。 将以下预处理应用于每个图像: *像素数据的自动取向(带有Exif-Arientation剥离) *调整大小为640x640(拉伸) 没有应用图像增强技术。

    绝对给力的源码,在线音乐播放器完整项目.zip

    Android 毕业设计,Android 毕业设计,小Android 程设计,含有代码注释,新手也可看懂。毕业设计、期末大作业、课程设计、高分必看,下载下来,简单部署,就可以使用。 包含:项目源码、数据库脚本、软件工具等,该项目可以作为毕设、课程设计使用,前后端代码都在里面。 该系统功能完善、界面美观、操作简单、功能齐全、管理便捷,具有很高的实际应用价值。

    毕业设计-0-1背包问题动态规划模型Python代码.rar

    1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、本项目仅用作交流学习参考,请切勿用于商业用途。

    保质量的周期边界2dAllen-Cahn方程求解器:纯隐格式迭代解

    谁喜欢谁下载,没啥商业价值,comsol也能做,不过我这产量更大

    Python项目-游戏源码-10 植物大战僵尸.zip

    Python课程设计,含有代码注释,新手也可看懂。毕业设计、期末大作业、课程设计、高分必看,下载下来,简单部署,就可以使用。 包含:项目源码、数据库脚本、软件工具等,该项目可以作为毕设、课程设计使用,前后端代码都在里面。 该系统功能完善、界面美观、操作简单、功能齐全、管理便捷,具有很高的实际应用价值。

    实现获取视频的缩略图(ThumbnailUtils),并且播放.zip

    Android 毕业设计,Android 毕业设计,小Android 程设计,含有代码注释,新手也可看懂。毕业设计、期末大作业、课程设计、高分必看,下载下来,简单部署,就可以使用。 包含:项目源码、数据库脚本、软件工具等,该项目可以作为毕设、课程设计使用,前后端代码都在里面。 该系统功能完善、界面美观、操作简单、功能齐全、管理便捷,具有很高的实际应用价值。

    推箱子Python小游戏

    推箱子Python小游戏

    基于ssm的新媒体视域下的中国古诗词展演源代码(java+vue+mysql+说明文档+LW).zip

    该新媒体视域下的中国古诗词展演主要为管理员和用户两类用户角色提供需求,管理员在后台可以对系统进行全面管理,用户在前台可以进行查看系统信息,注册登录,查询校园失物,评论,下载校园失物等操作。 项目包含完整前后端源码和数据库文件 环境说明: 开发语言:Java 框架:ssm,mybatis JDK版本:JDK1.8 数据库:mysql 5.7 数据库工具:Navicat11 开发软件:eclipse/idea Maven包:Maven3.3 部署容器:tomcat7

    Matlab实现PSO-BiLSTM-Attention粒子群算法优化双向长短期记忆神经网络融合注意力机制多特征分类预测(含完整的程序,GUI设计和代码详解)

    内容概要:本文介绍了使用MATLAB实现PSO-BiLSTM-Attention粒子群优化双向长短期记忆神经网络融合注意力机制的多特征分类预测模型。通过PSO优化BiLSTM模型的超参数、引入注意力机制增强模型的特征提取能力,提升了多维度数据的分类精度。模型在金融风险预测、医疗健康预测、交通流量预测等多个领域具有广泛的应用前景。项目详细描述了模型架构、代码实现、训练与优化、模型评估与可视化、以及GUI界面设计等方面的内容。 适合人群:具备一定编程基础,工作1-3年的数据科学家和机器学习工程师。 使用场景及目标:① 金融、医疗、交通等领域的多特征分类预测任务;② 结合PSO优化BiLSTM超参数、引入注意力机制,提升模型预测准确度。 阅读建议:本文详细讲解了模型的理论背景、算法实现和应用案例,适合希望深入理解深度学习和优化算法的读者。建议结合代码和实际数据进行实验,以便更好地掌握模型的设计和优化过程。

    Java项目-基于SSM的物资管理系统项目源码.zip

    Java项目-基于SSM的物资管理系统项目源码

    Video_2024-12-18_000023.wmv

    Video_2024-12-18_000023.wmv

    Python项目-自动办公-26 Python从原Excel表中抽出数据存入同一文件的新的Sheet.zip

    Python课程设计,含有代码注释,新手也可看懂。毕业设计、期末大作业、课程设计、高分必看,下载下来,简单部署,就可以使用。 包含:项目源码、数据库脚本、软件工具等,该项目可以作为毕设、课程设计使用,前后端代码都在里面。 该系统功能完善、界面美观、操作简单、功能齐全、管理便捷,具有很高的实际应用价值。

    基于ssm的家居商城系统的设计与实现+jsp源代码(完整前后端+mysql+说明文档+LW).zip

    系统实现: 用户功能模块:用户点击进入到系统操作界面,可以对主页、个人中心、我的收藏管理、订单管理等功能模块,我的收藏管理:通过列表可以获取用户ID、收藏ID、表名、收藏名称、收藏图片信息并进行修改操作 管理员功能模块:管理员通过用户名和密码填写完成后进行登录。管理员登录成功后进入到系统操作界面,可以对主页、个人中心、用户管理、商品分类管理、商品信息管理、系统管理、订单管理等功能模块进行相对应操作。 项目包含完整前后端源码和数据库文件 环境说明: 开发语言:Java 框架:ssm,mybatis JDK版本:JDK1.8 数据库:mysql 5.7 数据库工具:Navicat11 开发软件:eclipse/idea Maven包:Maven3.3 服务器:tomcat7

    STM32F103单片机采集温湿度及SPI FLASH数据保存并通过BC260-NBIOT模块上传数据到华为云物联网平台代码

    1、嵌入式物联网单片机项目开发实战。例程经过精心编写,简单好用。 2、代码使用KEIL 标准库开发,当前在STM32F103运行,如果是STM32F103其他型号芯片,依然适用,请自行更改KEIL芯片型号以及FLASH容量即可。 3、软件下载时,请注意keil选择项是jlink还是stlink。 4、有偿指导v:wulianjishu666; 5、如果接入其他传感器,请查看发布的其他资料。 6、单片机与模块的接线,在代码当中均有定义,请自行对照。 7、若硬件差异,请根据自身情况调整代码,程序仅供参考学习。 8、代码有注释说明,请耐心阅读。

    基于ssm的学习视频资源库的系统源代码(java+jsp+mysql+说明文档).zip

    项目包含完整前后端源码和数据库文件 环境说明: 开发语言:Java 框架:ssm,mybatis JDK版本:JDK1.8 数据库:mysql 5.7 数据库工具:Navicat11 开发软件:eclipse/idea Maven包:Maven3.3 部署容器:tomcat7

    Java项目-基于SSM的网上淘书吧.zip

    Java项目-基于SSM的网上淘书吧

    Oracle 19c 中的闪回技术详解及实战

    内容概要:本文详细介绍了 Oracle 19c 中的闪回技术,包括闪回查询、闪回事务查询、闪回丢弃、闪回表、闪回数据库和闪回归档。具体讲解了每种闪回技术的原理、配置方法、操作步骤和限制条件,并提供了具体的实例和 SQL 命令。目的是帮助数据库管理员和开发人员理解和掌握如何利用这些技术来提高数据恢复和错误修复的能力,减少数据库管理的复杂性和风险。 适合人群:Oracle 数据库管理员、数据库开发人员及维护人员。 使用场景及目标:① 使用闪回技术快速恢复因误操作或其他错误导致的数据丢失;② 配置闪回技术以实现高效的数据库恢复;③ 在日常运维中监控和管理闪回操作。 其他说明:本文不仅提供了理论上的解释,还包含了实际操作的示例,以便读者能够更好地理解和应用这些技术。

Global site tag (gtag.js) - Google Analytics