博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
单例模式总结
阅读量:7129 次
发布时间:2019-06-28

本文共 4459 字,大约阅读时间需要 14 分钟。

1.简介

单例模式是软件开发中常用的一种设计模式,它要确保某个类在系统中只有唯一的一个实例

2.应用场景

a.共享资源,保证数据的一致性,如配置文件

b.创建对象耗时或者消耗资源过多,但又经常使用到,如数据库连接池、线程池等
c.工具类对象

3.实现方式

a.饿汉式

public class SingletonOne {    private static final SingletonOne singletonOne = new SingletonOne();    private SingletonOne() {        if (singletonOne != null) {            // 防止反射获取多个对象的漏洞            throw new RuntimeException("instance has init");        }    }    public static SingletonOne getInstance() {        return singletonOne;    }    // 防止反序列化获取多个对象的漏洞    private Object readResolve() throws ObjectStreamException {        return singletonOne;    }}复制代码

优点:

类加载时完成对象实例化,避免多线程下的同步问题,同时获取单例速度较快

缺点:

资源利用效率不高,单例可能一直用不到,但是已经初始化了,造成资源浪费

b.懒汉式

public class SingletonTwo {    private static SingletonTwo singletonTwo = null;    private SingletonTwo() {        if (singletonTwo != null) {            throw new RuntimeException("instance has init");        }    }    public static SingletonTwo getInstance(){        if (singletonTwo == null) {            singletonTwo = new SingletonTwo();        }        return singletonTwo;    }    // 防止反序列化获取多个对象的漏洞    private Object readResolve() throws ObjectStreamException {        return singletonTwo;    }}复制代码

优点:

懒加载,第一次调用时实例化对象,资源利用率高,后续调用速度快

缺点:

多线程场景下,会产生多个对象

c.懒汉式+锁

public class SingletonThree {    private static SingletonThree singletonThree = null;    private SingletonThree() {        if (singletonThree != null) {            throw new RuntimeException("instance has init");        }    }    public static SingletonThree getInstance() {        synchronized (SingletonThree.class) {            if (singletonThree == null) {                singletonThree = new SingletonThree();            }        }        return singletonThree;    }    // 防止反序列化获取多个对象的漏洞    private Object readResolve() throws ObjectStreamException {        return singletonThree;    }}复制代码

优点:

通过加锁解决多线程场景下产生多个实例的问题

缺点:

加锁后,每次调用方法都需要获取锁,影响性能,其实只需要在第一次实例化对象时调用对象

d.双重检查锁(DCL)

public class SingletonFour {    private static SingletonFour singletonFour = null;    private SingletonFour() {        if (singletonFour != null) {            throw new RuntimeException("instance has init");        }    }    public static SingletonFour getInstance() {        if (singletonFour == null) {            synchronized (SingletonFour.class) {                if (singletonFour == null) {                    singletonFour = new SingletonFour();                }            }        }        return singletonFour;    }    // 防止反序列化获取多个对象的漏洞    private Object readResolve() throws ObjectStreamException {        return singletonFour;    }}复制代码

优点:

未实例化时才会去竞争锁来实例化,实例化后直接返回实例对象,提升性能同时解决多线程的同步问题

缺点:

由于jvm的指令重排问题,在多线程场景下会引用一个未初始化完成的对象,导致系统出现问题,具体原因如下:对象创建时,其实是分三步完成的,1)在堆上给对象分配内存,2)调用构造方法初始化此对象,3)将对象引用指向对应内存地址,由于jvm的指令重排机制,这三步可能不是顺序执行的,如果是1)3)2)顺序,3)完成2)还没完成时,另一个线程判断对象不为null会直接返回未初始化完全的对象,导致系统出现问题

e.双重检查锁(优化)

public class SingletonFour {    private static volatile SingletonFour singletonFour = null;    private SingletonFour() {        if (singletonFour != null) {            throw new RuntimeException("instance has init");        }    }    public static SingletonFour getInstance() {        if (singletonFour == null) {            synchronized (SingletonFour.class) {                if (singletonFour == null) {                    singletonFour = new SingletonFour();                }            }        }        return singletonFour;    }    // 防止反序列化获取多个对象的漏洞    private Object readResolve() throws ObjectStreamException {        return singletonFour;    }}复制代码

优点:

在双重检查锁的基础增加了volatile关键字,禁止指令重排,避免出现引用未初始化完成的对象

缺点:

禁用指令重排后,jvm的一些代码优化措施会无效,影响性能

f.内部静态类

public class SingletonFive {    private SingletonFive() {        // 防止反射获取多个对象的漏洞        if (Singleton.singleton != null) {            throw new RuntimeException("instance has init");        }    }    private static class Singleton {        private static final SingletonFive singleton = new SingletonFive();    }    public static SingletonFive getInstance() {        return Singleton.singleton;    }    // 防止反序列化获取多个对象的漏洞    private Object readResolve() throws ObjectStreamException {        return Singleton.singleton;    }}复制代码

推荐使用

懒加载,线程安全

内部类不会随着外部类初始化,只会在调用getInstance时才会初始化,而由于类的初始化<clinit>()会被加锁,所以也是线程安全的

g.枚举

public enum SingletonSix {    singleton;  }复制代码

推荐使用

只需调用SingletonSix. singleton,写法简单且线程安全,还能防止反序列化重新创建对象,只会存在一个实例

4.结语

单例模式实现方式常见有以上这几种,推荐使用内部静态类和枚举的方式实现单例。同时在使用过程中要注意避免反射和反序列化创建多个对象

转载于:https://juejin.im/post/5c936762f265da611167464d

你可能感兴趣的文章
C#设计模式之二十三解释器模式(Interpreter Pattern)【行为型】
查看>>
js处理中文乱码记录/nodejs+express error 413
查看>>
基于Keepalived实现LVS双主高可用集群
查看>>
SqlServer 使用脚本创建分发服务及事务复制的可更新订阅
查看>>
什么是Floating (浮动)规则?
查看>>
分布式文件系统-FastDFS
查看>>
HTML5 rotate 做仪表盘
查看>>
为什么说荆州松滋刘氏采穴堂是刘开七、刘广传的后裔
查看>>
React中使用Ant Table组件
查看>>
第四篇 快速、轻量、可扩展、易于使用的EmEditor
查看>>
MySQL删除小写记录
查看>>
用shell脚本收集查询IP信息的网站
查看>>
shiro整合oauth
查看>>
超级网管员——网络管理
查看>>
AjaxControltoolkit(工具包)安装步骤说明
查看>>
利用组策略进行的一次Windows主机安全整改
查看>>
Ruby语法学习笔记(1)
查看>>
Windows Phone 7 使用选择器(Chooser)
查看>>
QOS 之 WRED
查看>>
ASP.NET MVC5 知识点整理
查看>>