深度讲解23种设计模式,力争每种设计模式都刨析到底。废话不多说,开始第一种设计模式 - 单例。

  作者已知的单例模式有8种写法,而每一种写法,都有自身的优缺点。

1,使用频率最高的写法,废话不多说,直接上代码

/**
* @author xujp
* 饿汉式 静态变量 单例
*/
public class Singleton  implements Serializable {

    private static final long serialVersionUID = 1L;

    private final static Singleton instance = new Singleton();

    private Singleton(){}
public static Singleton getSingleton(){
return instance;
} private String tmp; public String getTmp() {
return tmp;
} public void setTmp(String tmp) {
this.tmp = tmp;
}
}

 new Singleton() 的执行时机 - > 类加载时

 这种方法是最通用的单例实现,也是笔者常用的,但这种方法有一些缺点:

 1)内存方面,如果单例中的内容很多,会在类加载时,就占用java虚拟机(这里专指HotSpot)空间。

 2)序列化以及反序列化问题,如果这个单例类实现了序列化接口Serializable,那么可以通过反序列化来破坏单例。

 通过反序列化破坏单例:

public static void main(String[] args) throws IOException, ClassNotFoundException {
Singleton singleton=null;
Singleton singletonNew=null; singleton=Singleton.getSingleton(); singleton.setTmp("123"); ByteArrayOutputStream bos=new ByteArrayOutputStream();
ObjectOutputStream oos=new ObjectOutputStream(bos);
oos.writeObject(singleton); ByteArrayInputStream bis=new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois=new ObjectInputStream(bis);
singletonNew= (Singleton) ois.readObject(); singleton.setTmp("456"); System.out.println(singletonNew.getTmp());
System.out.println(singleton.getTmp());
System.out.println(singleton==singletonNew);
}

  输出结果为:

  false

  123

  456

  从这里例子中我们可以看到单例被破坏了,也就不能保证单例的唯一性。

2,第一种方案的变种

/**
* @author xujp
* 饿汉式 静态代码块 单例
*/
public class Singleton implements Serializable { private static final long serialVersionUID = 1L; private final static Singleton instance; static {
instance = new Singleton();
} private Singleton(){} public static Singleton getSingleton(){
return instance;
}
}

 其实这种方法和第一种方法,几乎没有什么区别。

3,线程不安全的写法 - 1

/**
* @author xujp
* 懒汉式 单例 线程不安全
*/
public class Singleton implements Serializable { private static final long serialVersionUID = 1L; private static Singleton instance; private Singleton(){} public static Singleton getSingleton(){
if(null == instance) {
instance = new Singleton();
}
return instance;
}
}

 这种写法,虽然实现了懒加载,节省了内存,但线程不安全。

 假设有两个线程,并假设 new Singleton() 耗时2秒,0秒时,线程1执行new,然后去等待,1秒时,线程2执行if判断,

这个时候判断结果就是true,这样就会出现两个Singleton对象,完美破坏掉了单例。

4,线程不安全的写法 - 2

/**
* @author xujp
* 懒汉式 单例 代码块加锁 线程不安全
*/
public class Singleton implements Serializable { private static final long serialVersionUID = 1L; private static Singleton instance; private Singleton(){} public static Singleton getSingleton(){
if(null == instance) {
synchronized (Singleton.class) {
instance = new Singleton();
}
}
return instance;
}
}

 这种写法虽然在new Single()时,增加了锁,但这个锁,并不能阻止单例被破坏,所以这种写法错误。

 同样,假设有两个线程,线程1执行到synchronized时,线程2执行if判断,这个时候判断结果就是true,

这样就会出现两个Singleton对象,同样完美破坏掉了单例。

5,线程安全,但资源消耗过多

/**
* @author xujp
* 懒汉式 单例 方法加锁 线程不安全
*/
public class Singleton implements Serializable { private static final long serialVersionUID = 1L; private static Singleton instance; private Singleton(){} synchronized public static Singleton getSingleton(){
if(null == instance) {
instance = new Singleton();
}
return instance;
}
}

 这种写法确实能够保证线程安全,但synchronized属于方法锁,而方法锁回锁定对象,导致性能低下。

6,相对完美的写法 - 1

/**
* @author xujp
* 懒汉式 单例 代码加锁 线程安全
*/
public class Singleton implements Serializable { private static final long serialVersionUID = 1L; private static volatile Singleton instance; private Singleton(){} public static Singleton getSingleton(){
if(null == instance) {
synchronized (Singleton.class) {
if(null == instance) {
instance = new Singleton();
}
}
}
return instance;
}
}

双检查这种写法,在多线程问题上,属实没有问题,synchronized也没有锁定对象,而且也优化了锁资源开销问题。

7,相对完美的写法 - 2

/**
* @author xujp
* 懒汉式 单例 静态内部类 线程安全
*/
public class Singleton implements Serializable { private static final long serialVersionUID = 1L; private static class SingletonInstance{
private static Singleton instance = new Singleton();
}
private Singleton(){} public static Singleton getSingleton(){
return SingletonInstance.instance;
}
}

使用静态内部类来实现单例,主要借助JVM机制,静态内部类初始化的时候,其他线程无法进入,从而避免了多线程问题。

而且静态内部类不会直接初始化,从而减轻了内存开销。

8,完美写法

/**
* @author xujp
* 枚举实现单例
*/
public enum Singleton {
SINGLETON;
private String property = "hello ca fe ba be";
public void doSomeThing(){
System.out.println(property);
}
}

这种写法用枚举解决多线程问题,而且时唯一一种解决序列化问题的写法。

改写法出自大神Josh Bloch,如果有兴趣可以去查看一下他的资料。

总结:

1,1和2写法虽然是饿汉式,没有实现懒加载,也没有100%保证单例,但却是我们最常用的写法,

 因为,单例对象通常占用空间不会很大,而且程序都由程序员自己管理,被反序列的危险性不高。

2,3和4写法实现了懒加载,减少了内存开销,但不能使用,因为多线程开发,是我们常见的开发。

3,5写法使用了方法锁,会将对象锁住,会导致性能大打折扣。

4,6和7写法,懒加载、性能都非常完美,缺点只有一个,那就是序列化问题。

5,8写法,笔者暂未发现缺点。

实际开发中,无论是使用1、2写法,还是使用6、7写法,亦或是使用8写法,都是可以的。


  

java设计模式 - 单例模式(干货)的更多相关文章

  1. java设计模式单例模式 ----懒汉式与饿汉式的区别

    常用的五种单例模式实现方式 ——主要: 1.饿汉式(线程安全,调用率高,但是,不能延迟加载.) 2.懒汉式(线程安全,调用效率不高,可以延时加载.) ——其他: 1.双重检测锁式(由于JVM底层内部模 ...

  2. Java设计模式の单例模式

    -------------------------------------------------- 目录 1.定义 2.常见的集中单例实现 a.饿汉式,线程安全 但效率比较低 b.单例模式的实现:饱 ...

  3. JAVA设计模式-单例模式(Singleton)线程安全与效率

    一,前言 单例模式详细大家都已经非常熟悉了,在文章单例模式的八种写法比较中,对单例模式的概念以及使用场景都做了很不错的说明.请在阅读本文之前,阅读一下这篇文章,因为本文就是按照这篇文章中的八种单例模式 ...

  4. Java设计模式 - - 单例模式 装饰者模式

    Java设计模式 单例模式 装饰者模式 作者 : Stanley 罗昊 [转载请注明出处和署名,谢谢!] 静态代理模式:https://www.cnblogs.com/StanleyBlogs/p/1 ...

  5. 【设计模式】Java设计模式 - 单例模式

    [设计模式]Java设计模式 - 单例模式 不断学习才是王道 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 分享学习心得,欢迎指正,大家一起学习成长! 原创作品,更多关注我CSDN: ...

  6. Java 设计模式 —— 单例模式

    1. 概念: 单例模式是一种常用的软件设计模式.核心结构中只包含一个被称为单例的特殊类.通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源.如果 ...

  7. Java设计模式 - 单例模式 (懒汉方式和饿汉方式)

    概念: Java中单例模式是一种常见的设计模式,单例模式的意思就是只有一个实例.单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例.这个类称为单例类. 单例模式的写法有好几种,这 ...

  8. java设计模式——单例模式(一)

    一. 定义与类型 定义:保证一个类仅有一个实例,并提供一个全局访问点 类型:创建型 二. 适用场景 想确保任何情况下都绝对只用一个实例 三. 优缺点 优点: 在内存里只有一个实例,减少了内存开销 可以 ...

  9. JAVA设计模式--单例模式

    单例设计模式 Singleton是一种创建型模式,指某个类采用Singleton模式,则在这个类被创建后,只可能产生一个实例供外部访问,并且提供一个全局的访问点. 核心知识点如下: (1) 将采用单例 ...

随机推荐

  1. 「 深入浅出 」java集合Collection和Map

    本系列文章主要对java集合的框架进行一个深入浅出的介绍,使大家对java集合有个深入的理解. 本篇文章主要具体介绍了Collection接口,Map接口以及Collection接口的三个子接口Set ...

  2. 实用代码|javaMail发送邮件(文末重磅资源!)

    每天进步一点点,距离大腿又近一步!阅读本文大概需要5分钟 JavaMail发送邮件,简单实用,了解一下呗~ 1.开启邮箱MAP/SMTP服务,获取第三方授权码 以QQ邮箱为例 2.主要代码 maven ...

  3. 01-前言&WEB标准

    人生苦短,要学就只学有用的 [前端教学-前言] 初识web开发 我们先来认识一下web前端 其实前端的工作,大体的概括就是:根据美工给的设计稿,变成web网页,使用后天的接口实现数据的渲染,要是高端一 ...

  4. python文件夹中文件读取踩坑

    Q: 进行数据集图片预处理时,初始命名如下图(Fig1左),发现读取文件时,读取的结构并非如所设想的那样顺序读取 Fig 1 A: pyhton读取文件的时候,按照文件名的ascii码中的顺序进行逐位 ...

  5. ios---图片缩放

    1.设置scrollview的代理 2.实现如下方法 -(UIView )viewForZoomingInScrollView:(UIScrollView )scrollView{ return se ...

  6. python学习Day02

    [主要内容] 1. 循环. while循环 while 条件: 代码块(循环体) 执行流程: 1. 判断条件是否为真. 如果真. 执行代码块 2. 再次判断条件是否为真...... 3. 当条件为假. ...

  7. Unreal Engine 4 蓝图完全学习教程(四)—— 变量与计算

    Ⅰ.值的基础类型 ①文本.字符串(Text.String):文本类型的值. ②整型.浮点型(Int.Float):数字类型的值. ③布尔型(Bool):表示“真或假”二者选其一的状态. Ⅱ.加法运算 ...

  8. LIBCMTD.lib与libcpmtd冲突的解决方法。

    error: 1>uafxcwd.lib(afxmem.obj) : error LNK2005: "void * __cdecl operator new(unsigned int) ...

  9. ARTS Week 1

    Oct 28,2019 ~ Nov 3,2019 Algorithm 本周的学习的算法是二分法.二分法可以用作查找即二分查找,也可以用作求解一个非负数的平方根等.下面主要以二分查找为例. 为了后续描述 ...

  10. Luinx安装RocketMQ

    一.RocketMQ环境 准备两台虚拟机,分别为master01 和master02 二.安装JDK(两台虚拟机相同步骤) 1. 检查当前虚拟机环境有没有JDK rpm -qa|grep java ( ...