单例模式简介

90%以上的设计模式都或多或少的应用了接口和抽象类,而单例比较特殊,并没有接口的应用。

单例Singleton指仅仅被实例化一次的类。通常被用来代表那些本质上唯一的系统组件。————《Effective Java》

数据库连接获取类的对象可以是单例的。

单例的意思就是在内存中只有一个对象。而单例和static有是有区别的,static是用来修饰类中的成员变量和成员方法的,而单例则属于对象的层面。

单例的实现

思考:如何在内存中只有一个对象?——不能让外界随意的实例化(new)。封装思想中有一个叫“属性私有化,方法公开化”的概念,在需要单例的类中自己去实例化。这就是单例的两个条件:不能被外界实例化;根据封装的特征属性私有化,方法公开化。

外界不能实例化,实现的方式就是将需要设置为单例的类(以下简称:单例类)的构造器设置为private。

注意:如果电脑上安装了多个虚拟机的话,那么实际上并不是真正意义上的单例,而是每个虚拟机会有一个单例。

懒汉单例模式:

懒汉单例模式在第一次使用对象的时候才会去创建对象,但最简单的懒汉单例存在多线程安全问题,依然有可能创建多个实例。

/*** 懒汉单例模式Demo<br>懒汉单例是在运行时调用的一种行为。* 单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。<br>* 这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。<br>* 这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。<br>* 这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。<br>* * 类名:LazySingleton<br>* 作者: mht<br>* 日期: 2018年3月18日-下午8:41:01<br>*/
public class LazySingleton {/** 属性私有化 */private static LazySingleton instance = null;/** 构造器私有化 */private LazySingleton() { }/** 公开获取单例对象 */public static LazySingleton getInstance() {if (instance == null) {instance = new LazySingleton();}return instance;}
}

饿汉单例模式:

饿汉单例会在类加载的时候即实例化对象,这与懒汉单例的创建时机截然不同,不存在线程安全问题,

但缺点是需要提前占用内存资源。

/*** 类名:EagerSingleton<br>* 作者: mht<br>* 日期: 2018年3月18日-下午8:41:01<br>*/
public class EagerSingleton {/** 使用静态常量*/private static final EagerSingleton instance = new EagerSingleton();private EagerSingleton() {System.out.println("这是构造器:LazySingleton()");}public static EagerSingleton getInstance() {return instance;}public void doSomething() {System.out.println("EagerSingleton is doing something now ...");}
}

懒汉+饿汉(静态内部类):

该方法通过私有静态内部类来实现单实例延迟加载,因为静态内部类不会因为外部类的加载而加载,所以可以在第一次使用静态类的时候才执行加载。
优点是:不仅可以达到懒汉式的延迟加载,使类的加载速度提升,避免一开始占用过多内存,又具备线程安全性,无需判断,性能上也会更快一些。

/*** 作者: mht<br>* 日期: 2018年3月19日-下午9:55:52<br>*/
public class Singleton {private Singleton () {System.out.println("Singleton...");}private static class SingletonInstance {private static final Singleton s = new Singleton();}public static Singleton getInstance() {return SingletonInstance.s;}
}

双重检查-单例模式(线程安全的懒汉式单例):

双重检查单例模式 使用双重检查同步延迟加载来创建单例的做法是一个非常优秀的做法, 其不但保证了单例(线程安全),而且切实提高了程序的运行效率。

实现原理:

Java语言提供了一种较synchronized稍弱的同步机制,volatile,用来确保将变量的更新操作通知到其他线程。当把变量声明为volatile类型后,编译器运行时都会注意到这个变量是共享的,因此不会将该变量上的操作与其他内存操作一起重排序。volatile变量不会被缓存在寄存器或者对其他处理器不可见的地方,因此在读取volatile类型的变量时总会返回最新写入的值。

getInstance()方法描述:

为了在保证单例的前提下提高运行效率,我们需要对单例对象dcs进行第二次检查,目的是避开过多的同步(因为这里的同步只需在第一次创建实例时才同步,一旦创建成功,以后获取实例时就不需要同步获取锁了)。这种做法无疑是优秀的,但是我们必须注意一点:必须使用volatile关键字修饰单例对象

public class DoubleCheckSingleton {// 使用volatile关键字防止重排序,因为 new Instance()是一个非原子操作。private static volatile DoubleCheckSingleton dcs;private DoubleCheckSingleton() {}public static DoubleCheckSingleton getInstance() {if (dcs == null) {synchronized (DoubleCheckSingleton.class) {// 只需在第一次创建实例时才同步if (dcs == null) {dcs = new DoubleCheckSingleton();}}}return dcs;}
}

注册单例模式:

public class RegisterSingleton<T> {/* 注册表 */private static Map<String, Object> regMap = new HashMap<>();// 类加载过程中,静态初始化static{Connection conn = new Connection();UserService us = new UserService();// 初始化注册表regMapregMap.put(conn.getClass().getName(), conn);regMap.put(us.getClass().getName(), us);}private RegisterSingleton() { }public synchronized static Object getInstance(String key) {if (key == null) return null;try {if (regMap.get(key) == null) {// 这里注意,此Demo是以类名作为key传入map中的,因此,这里的传值也是相关方法,通过类名找到类,然后在进行自动实例化regMap.put(key, Class.forName(key).newInstance());}return regMap.get(key);} catch (ClassNotFoundException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}return null;}
}

测试:

package design.pattern;public class testSingleton {public static void main(String[] args) {UserService us = (UserService) RegisterSingleton.getInstance(UserService.class.getName());Connection conn1 = (Connection) RegisterSingleton.getInstance(Connection.class.getName());Connection conn2 = (Connection) RegisterSingleton.getInstance(Connection.class.getName());System.out.println(us);System.out.println(conn1);System.out.println(conn2);System.out.println(conn1 == conn2);}
}

运行结果:

design.pattern.UserService@15db9742
design.pattern.Connection@6d06d69c
design.pattern.Connection@6d06d69c
true

对单例模式应用的理解:

在spring框架中大多数对象的使用都是单例模式的,比如获取用户User对象的方法类UserDao和UserService都是单例的,包括controller,他们默认都单例的。因为实际上我们真正需要多个对象的是User而并不是获取User的方法类。

这也就充分解释了Spring注解中的@Autowired自动注入对象的实现方式。细心的我们也都可以发现,这些自动注入的对象一般都是实现某种业务的中间类对象,而并不是最终我们需要的对象,因此,为了避免频繁的创建这些“中间件”对象占用不必要的内存空间,都是以单例的形式来创建的。另外,数据库连接对象Connection也一定是单例的。

单例模式的误解:

单例会导致线程阻塞吗?不会的。

举个生活中的例子:饭店只有一个菜谱,但是有多个厨师,多个厨师共享这份菜谱,并同时做一道菜,彼此之间是没有影响的;老师在黑板上写下一个数学题,同学们在下面计算结果,黑板就是内存,数学题就是单例对象,同学们就是各个线程,计算过程之间是没有影响的。

单例模式的优点:

1.内存中只有一个对象,节省内存空间

2.避免频繁的创建和销毁对象,可以提高性能

3.避免对共享资源的多重占用,简化访问

4.为整个系统提供一个全局访问点

单例模式的应用场景:

在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡驱动程序对象常被设计为单例的。实际上,这些应用都或多或少具有资源管理器的功能。

其核心在于为整个系统提供一个唯一的实例,其应用场景包括但不仅限于一下几种:

1.有状态的工具类

2.频繁访问数据库或文件的对象

参考:

《彻头彻尾理解单例模式以及其在多线程环境中的应用》

Java常用设计模式————单例模式相关推荐

  1. 初学Java常用设计模式之——原型模式

    声明:转载请附上原文链接 提示:标题序号从3开始,是照应不同设计模式笔记发布的顺序而定的,比如,第上一篇文章 初学Java常用设计模式之--工厂模式 序号从2开始. 标题后面之所以加上了解,是因为相对 ...

  2. 初学Java常用设计模式之——工厂模式

    声明:转载请附上原文链接 提示:标题序号从2开始,是照应不同设计模式笔记发布的顺序而定的,比如,第上一篇文章 初学Java常用设计模式之--单例模式 序号从1开始 2. 工厂模式(常用) ⼯⼚模式介绍 ...

  3. Java常用设计模式————原型模式(一)

    介绍 原型模式(Prototype Pattern):用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象. 原型模式用于创建重复的对象,同时又能保证性能.当直接创建对象的代价比较大时,则采用 ...

  4. java常用设计模式文章收集

    java常用设计模式文章收集 java设计模式--接口模式 java设计模式--抽象工厂模式 java设计模式--工厂模式 Java设计模式--迭代器模式 Java设计模式--原型模式 JAVA设计模 ...

  5. 初学Java常用设计模式之——装饰器模式

    声明:转载请附上原文链接 提示:标题序号从8开始,是照应不同设计模式笔记发布的顺序而定的,比如,上一篇文章 初学Java常用设计模式之--桥接模式和组合模式 序号从7开始. 8. 装饰器设计模式(重点 ...

  6. JAVA常用设计模式(一、单例模式、工厂模式)

    JAVA设计模式之单例模式 import java.util.HashMap; import java.util.Map;/*** 设计模式之单例模式* 单例模式(Singleton Pattern) ...

  7. 初学Java常用设计模式之——单例模式

    0. 常⻅的三⼤设计模式分类 创建型模式:提供了⼀种在创建对象的同时隐藏创建逻辑的⽅式,使 得程序在判断针对某个给定实例需要创建哪些对象时更 加灵活,比如: 常用4个:⼯⼚模式.抽象⼯⼚模式.单例模式 ...

  8. java常用设计模式一:单例模式

     1.饿汉式 package singleton.demo;/*** @author Administrator* @date 2019/01/07*/ public class Singleton ...

  9. java常用设计模式 看了她,改变你的一生~~~

    设计模式: 一个程序员对设计模式的理解: "不懂"为什么要把很简单的东西搞得那么复杂.后来随着软件开发经验的增加才开始明白我所看到的"复杂"恰恰就是设计模式的精 ...

最新文章

  1. 中华图标网--http://sinoLogo.cn
  2. Python “with” keyword
  3. windows securecrt下文件复制到linux,SecureCRT在linux与windows本地互传文件
  4. 第三章 线性表---顺序存储结构
  5. 超声声场模拟_超声全聚焦(TFM)简介
  6. 信息学奥赛一本通(1077:统计满足条件的4位数)
  7. macos中xcode使用opencv,错误总结
  8. Java常见bean mapper的性能及原理分析
  9. nginx之反向代理、负载均衡
  10. [codewars] - int32 to IPv4 二进制十进制 ip地址转换
  11. macOS Recovery中修复磁盘的具体操作方法
  12. Python 定时任务框架
  13. CF1090H Linearization
  14. Win10与苹果AirDrop(隔空投送)
  15. 深信服X计划-云计算基础1
  16. 话题 | 手机充电越充越少,90%的人都遇过这些囧事,有你吗?
  17. 关于龙蜥社区20个问题 |龙蜥问答第1期
  18. EasyWechat微信支付jsapi缺少参数 total_fee
  19. sql server 字符串和日期的转换函数
  20. 肯德基收款打印小票功能

热门文章

  1. python 唯一元素_检查所有元素在Python中是否唯一
  2. ThreadLocal内存溢出代码演示和原因分析!
  3. 对象复制的7种方法,还是Spring的最好用!
  4. 第 5-7 课:Java 中的各种锁和 CAS + 面试题
  5. turbo c相关文档
  6. 中html倒入css那么套路,CSS常用套路
  7. C#中IEnumerableT.GroupBy()的简单使用
  8. [深入学习C#]利用反射给对象赋值
  9. springboot python 开发效率比较-2018年Java开发值得学习的10大技术
  10. php ajax复选框是否选中的值,jquery-ajax - php+ajax分页时,checkbox复选框选中的问题...