单例顾名思义就是一个实例。类只有唯一一个实例,并提供给全局使用。解决了全局使用的类频繁地创建与销毁带了的消耗。

单例模式常用简单,但细究却又不简单,且往下看。

单例模式又可以分为

(1)懒汉式:需要使用实例时,才创建实例

(2)饿汉式:类加载时,就创建静态实例。

上代码

1、饿汉式,线程安全

 /**
* 饿汉式--线程安全
* 优点:没有加锁,执行效率会提高。
* 缺点:类加载时就初始化,浪费内存。
*/
public class UserHunger_Safe {
/**
* 对象实例
*/
private static UserHunger_Safe instance = new UserHunger_Safe(); /**
* 私有构造函数
*/
private UserHunger_Safe() {
} /**
* 对外提供公共获取实例方法(线程安全)
*
* @return
*/
public static UserHunger_Safe getInstance() {
return instance;
}
}

UserHunger_Safe.java

2、懒汉式,线程不安全

 /**
* 懒汉式单例模式
* 线程不安全
*/
public class UserLazy_Unsafe { /**
* 私有构造方法
*/
private UserLazy_Unsafe() {
} /**
* 对象实例
*/
private static UserLazy_Unsafe instance; /**
* 对外提供公共获取实例方法(线程不安全)
*
* @return
*/
public static UserLazy_Unsafe getInstance() {
if (instance == null) {
instance = new UserLazy_Unsafe();
}
return instance;
}
}

UserLazy_Unsafe.java

3、懒汉式--方法加锁synchronized,线程安全

 /**
* 懒汉式单例模式
* 优点:第一次调用才初始化,避免内存浪费。
* 缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率。
*/
public class UserLazy_Safe {
/**
* 私有构造方法
*/
private UserLazy_Safe() {
} /**
* 对象实例
*/
private static UserLazy_Safe instance; /**
* 对外提供公共获取实例方法(线程安全)
*
* @return
*/
public static synchronized UserLazy_Safe getInstance() {
if (instance == null) {
instance = new UserLazy_Safe();
}
return instance;
}
}

UserLazy_Safe.java

方法加锁,则每次调用都需要锁定,严重影响性能,这不是想要的。所以优化下得出第4中方案

4、双重校验锁(DCL,即 double-checked locking)

 /**
* 饿汉式+双重校验锁
*/
public class UserDCL_Safe {
/**
* 对象实例
*/
private static UserDCL_Safe instance; /**
* 私有构造函数
*/
private UserDCL_Safe() {
} /**
* 对外提供公共获取实例方法(线程安全)
*
* @return
*/
public static synchronized UserDCL_Safe getInstance() {
if (instance == null) {
//双重校验锁
synchronized (UserDCL_Safe.class) {
if (instance == null) {
instance = new UserDCL_Safe();
}
}
}
return instance;
}
}

UserDCL_Safe.java

如此一来,即便是多线程也能保证安全。如果只考虑静态域还可以使用内部静态类

5、静态内部类

 /**
* 登记式/静态内部类
* 线程安全
* 这种方式能达到双检锁方式一样的功效,但实现更简单。
* 这种方式只适用于静态域的情况,双检锁方式可在实例域需要延迟初始化时使用。
*/
public class UserStaticInnerClass_Safe {
private static class SingletonHolder{
private static final UserStaticInnerClass_Safe INSTANCE=new UserStaticInnerClass_Safe();//实例
} //私有构造函数
private UserStaticInnerClass_Safe(){}
public static final UserStaticInnerClass_Safe getInstance(){
return SingletonHolder.INSTANCE;
}
}

UserStaticInnerClass_Safe.java

至此,单例模式是不是绝对安全了?答案当然不是,在反射攻击下,外部依旧能做到非单例,不信,读者可以通过反射方式获得以上1,3,4,5例子的实例,就会发现依旧可以得到不同的实例。那有什么好的解决方案呢?

这个Effective Java 作者 Josh Bloch 大神给出了解决方案--使用枚举

6、枚举,最佳实现方式

 /**
* 枚举方式实现单例模式
*/
public enum UserEnum {
INSTANCE; /**
* 姓名
*/
private String name; /**
* 年龄
*/
private int age; /**
* 手机号码
*/
private String telephone; public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} public String getTelephone() {
return telephone;
} public void setTelephone(String telephone) {
this.telephone = telephone;
} @Override
public String toString() {
return "SingletonPattern_EnumType{" +
"name='" + name + '\'' +
", age=" + age +
", telephone='" + telephone + '\'' +
'}';
}
}

UserEnum.java

如此一来既保证了线程安全,也保证了反序列化安全

测试

 import java.lang.reflect.Constructor;

 public class SigletonPattern {
public static void main(String[] args) throws Exception {
//懒汉式单例--线程不安全
UserLazy_Unsafe ul1 = UserLazy_Unsafe.getInstance();
UserLazy_Unsafe ul2 = UserLazy_Unsafe.getInstance();
System.out.println(ul1 + "\n" + ul2);
System.out.println("===========懒汉式单例--线程不安全 End=========="); //懒汉式单例--线程安全.获取实例方法添加synchronized,加锁会影响效率。
UserLazy_Safe uls1 = UserLazy_Safe.getInstance();
UserLazy_Safe uls2 = UserLazy_Safe.getInstance();
System.out.println(uls1 + "\n" + uls2);
System.out.println("===========懒汉式单例--线程安全 End=========="); //懒汉式单例--登记式/静态内部类--线程安全。
UserStaticInnerClass_Safe usic = UserStaticInnerClass_Safe.getInstance();
UserStaticInnerClass_Safe usic2 = UserStaticInnerClass_Safe.getInstance();
System.out.println(usic + "\n" + usic2);
System.out.println("===========懒汉式单例--登记式/静态内部类--线程安全 End=========="); UserEnum user = UserEnum.INSTANCE;
//UserEnum user = new UserEnum();
user.setName("科技无国界");
user.setAge(0);
user.setTelephone("16895965423"); UserEnum levon = UserEnum.INSTANCE;
//UserEnum levon = new UserEnum();
levon.setName("联想--科技无国界");
levon.setAge(10);
levon.setTelephone("26895965423"); System.out.println(user);
System.out.println(levon); //通过反射获得对象
Constructor<UserEnum> constructor = UserEnum.class.getDeclaredConstructor();
constructor.setAccessible(true);
UserEnum sp = constructor.newInstance();
System.out.println(sp);
}
}

main方法

 UserLazy_Unsafe@1b6d3586
UserLazy_Unsafe@1b6d3586
===========懒汉式单例--线程不安全 End==========
UserLazy_Safe@4554617c
UserLazy_Safe@4554617c
===========懒汉式单例--线程安全 End==========
UserStaticInnerClass_Safe@74a14482
UserStaticInnerClass_Safe@74a14482
===========懒汉式单例--登记式/静态内部类--线程安全 End==========
SingletonPattern_EnumType{name='联想--科技无国界', age=10, telephone='26895965423'}
SingletonPattern_EnumType{name='联想--科技无国界', age=10, telephone='26895965423'}
Exception in thread "main" java.lang.NoSuchMethodException: UserEnum.<init>()
at java.lang.Class.getConstructor0(Class.java:3082)
at java.lang.Class.getDeclaredConstructor(Class.java:2178)
at SigletonPattern.main(SigletonPattern.java:39)

输出结果

示例源码:https://github.com/LF20160912/pattern

设计模式Design Pattern(2)--单例模式的更多相关文章

  1. 【Java】【设计模式 Design Pattern】单例模式 Singleton

    什么是设计模式? 设计模式是在大量的实践中总结和理论化之后的最佳的类设计结构,编程风格,和解决问题的方式 设计模式已经帮助我们想好了所有可能的设计问题,总结在这些各种各样的设计模式当中,也成为GOF2 ...

  2. 设计模式(Design Pattern)系列之.NET专题

    最近,不是特别忙,重新翻了下设计模式,特地在此记录一下.会不定期更新本系列专题文章. 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结. 使用 ...

  3. 设计模式Design Pattern(1)--简介

    什么是设计模式? 软件开发人员在长期实践中总结出来的解决特定问题的一套解决方案. 对象设计原则 计模式主要是基于以下的面向对象设计原则. 对接口编程而不是对实现编程. 优先使用对象组合而不是继承. 设 ...

  4. [Java复习] 设计模式 Design Pattern

    设计模式的六大原则 1.开闭原则(Open Close Principle) 对扩展开放,对修改关闭. 2.里氏代换原则(Liskov Substitution Principle) 任何基类可以出现 ...

  5. 设计模式のSingleton Pattern(单例模式)----创建模式

    单例模式没有什么好讲的,我们 举个例子 #region 单例定义 /// <summary> /// 类单例 /// </summary> private static Win ...

  6. 设计模式Design Pattern(4) -- 访问者模式

    什么是访问者模式? 一个对象有稳定的数据结构,却为不同的访问者提供不同的数据操作,对象提供接收访问者的方法,从而保证数据结构的稳定性和操作的多样性.也可以理解为,封装对象的操作方法,达到不改变对象数据 ...

  7. 设计模式Design Pattern(3) -- 责任链模式

    什么是责任链模式? 责任链模式(Chain of Responsibility Pattern):请求知道公开接口,但不知道那个具体类处理,这些具体处理类对象连接成一条链.请求沿着这条链传递,直到有对 ...

  8. Design Principle vs Design Pattern 设计原则 vs 设计模式

    Design Principle vs Design Pattern设计原则 vs 设计模式 来源:https://www.tutorialsteacher.com/articles/differen ...

  9. 简单工厂设计模式(Simple Factory Design Pattern)

    [引言]最近在Youtub上面看到一个讲解.net设计模式的视频,其中作者的一个理解让我印象很深刻:所谓的设计模式其实就是运用面向对象编程的思想来解决平时代码中的紧耦合,低扩展的问题.另外一点比较有见 ...

随机推荐

  1. leetcode 数组 (python)

    1.题目描述 给定一个范围在  1 ≤ a[i] ≤ n ( n = 数组大小 ) 的 整型数组,数组中的元素一些出现了两次,另一些只出现一次. 找到所有在 [1, n] 范围之间没有出现在数组中的数 ...

  2. WPF WebBrowser 加载 html ,出现安全警告, 运行 脚本和 activeX 控件,

    对于你的问题,只需要在你的HTML首行添加如下代码即可隐藏安全提示条: <!-- saved from url=(0014)about:internet --> 还有一个可选方案是使用Wi ...

  3. linux(centOS7)的基本操作(一) 概述

    linux服务器的连接 1.连接 window环境下需要安装XShell.XFtp等软件,暂时不表: macOS环境下直接用ssh命令登录即可,用以下任意一种 ssh [-p port] userna ...

  4. Openstack 实现技术分解 (1) 开发环境 — Devstack 部署案例详解

    目录 目录 前言 系统环境 Devstack 下载源码 配置文件 local.conf & localrc 简易的环境脚本 openrc 部署 Devstack 自动化部署流程 部署案例 单节 ...

  5. docker 安装mysql 并将文件挂载到本地

    首先准备好挂载的文件路径 执行mysql创建以及挂载的命令(这里还可以使用-e环境变量来创建新用户MYSQL_USER,MYSQL_PASSWORD) docker run -d -p : --res ...

  6. Java类加载器总结网址

    http://blog.csdn.net/gjanyanlig/article/details/6818655

  7. C语言Ⅰ博客作业02

    1. 这个作业属于哪个课程 C语言程序设计Ⅰ 这个作业要求在哪里 https://edu.cnblogs.com/campus/zswxy/CST2019-3/homework/8656 我在这个课程 ...

  8. C++ vector、list和deque的区别 (整理)

    1.vector数据结构 vector和数组类似,拥有一段连续的内存空间,并且起始地址不变.因此能高效的进行随机存取,时间复杂度为o(1);但因为内存空间是连续的,所以在进行插入和删除操作时,会造成内 ...

  9. Widget代码讲解

    参考:https://zhuanlan.zhihu.com/p/28225011 QT版本为5.12.4 1.main.cpp #include "widget.h" #inclu ...

  10. Tarjan水题系列(2):HNOI2012 矿场搭建

    题目: 煤矿工地可以看成是由隧道连接挖煤点组成的无向图.为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处.于是矿主决定在某些挖煤点设立救援出口,使得无论哪一个挖煤点坍塌之后 ...