1.概念
核心意图:确保一个类最多只有一个实例,并提供一个全局访问点。
常用场景:
1.项目中,读取配置文件的类,一般只有一个对象,没必要每次使用配置文件数据,每次new一个对象去读取。
2.数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源
3.在spring中,每个bean默认就是单例,这样做的优点是spring容器可以管理。
4.在spring Mvc框架/struts1框架中,控制器对象也是单例。
单例模式的优点:
- 由于单例模式只生成一个实例,减少了系统性能的开销,当一个对象的产生需要比较多的资源时,如读取配置,产生其他依赖时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决。
- 单例模式可以在系统设置全局的访问点,优化环共享资源访问,例如可以设计一个单例类,负责所有数据表的映射处理。
常见的五种单例模式实现方式:
--主要:
饿汉式(线程安全,调用效率高。但是,不能延迟记载)
懒汉式(线程安全,调用效率不高。但是,可以加在延迟)
--其他
双重检测琐式(由于jvm底层内部模型原因,偶尔会出问题。不建议使用)
静态内部类式(线程安全,调用效率高。但是,可以延时加载)
枚举单例(线程安全,调用效率高,不能延迟)
三要素:
(1)是单例模式的类只提供私有的构造函数,
(2)是类定义中含有一个该类的静态私有对象,
(3)是该类提供了一个静态的公有的函数用于创建或获取它本身的静态私有对象。
饿汉式:
单例对象立即加载,
饿汉式单例模式代码中,static变量会在类装载时初始化,此时也不会涉及多个线程对象访问该对象的问题。虚拟机保证只加载一次该类,肯定不会发生并发访问的问题。因此可以省略synchronized关键字。
如果只是加载本类,而不是要调用getInstance(),甚至永远没有调用,则会造成资源浪费!
代码示例:
public class SingletonDemo1 { private static SingletonDemo1 instance = new SingletonDemo1();//类初始化时,立即加载这个对象! private SingletonDemo1(){ } public static SingletonDemo1 getInstance(){ return instance; } }
懒汉式
单例对象延迟加载
延迟加载,懒加载,真正用的时候才加载
资源利用率高,但是每次调用getInstance()方法都要同步,并发效率低
代码示例:
public class SingletonDemo2 { private static SingletonDemo2 singletonDemo2; private SingletonDemo2(){} //私有化构造器 public static synchronized SingletonDemo2 getInstance(){ if (singletonDemo2==null){ singletonDemo2 = new SingletonDemo2(); } return singletonDemo2; } }
双重检测锁实现
这个模式将同步内容下放到if内部,提高了执行的效率,不必每次获取对象时都进行同步,只有第一次才同步创建了以后就没必要了
问题:由于编译器优化原因和jvm底层内部模型原因,偶尔出问题,不建议使用
代码示例:
public class SingletonDemo3 { private static SingletonDemo3 instance = null;//类初始化时,立即加载这个对象! private SingletonDemo3(){ } public static SingletonDemo3 getInstance(){ if (instance==null){ SingletonDemo3 sc; synchronized (SingletonDemo3.class){ sc=instance; if (sc==null){ synchronized (SingletonDemo3.class){ if (sc==null){ sc=new SingletonDemo3(); } } instance=sc; } } } return instance; } }
静态内部类实现方式
懒加载方式
外部没有static属性,则不会像饿汉式那样立即加载对象
只有真正调用getInstance(),才会加载静态内部类。加载类时是线程安全的。Instance是static final类型,保证了内存中只有一个实例存在,而且只能被赋值一次,从而保证了线程安全性。
兼备了并发高效和延迟加载的优势
代码示例:
public class SingletonDemo4 { private static class SingletonClassInstance { private static final SingletonDemo4 instance = new SingletonDemo4(); } private SingletonDemo4() { } public static SingletonDemo4 getInstance() { return SingletonClassInstance.instance; } }
枚举实现单例模式
没有延迟加载
实现简单
枚举本身就是单例模式。由JVM从根本上提供保障!避免通过反射和反序列化的漏洞!
代码示例:
public enum SingletonDemo5 { //枚举元素,本身就是单例 INSTANCE; //添加自己需要的操作 private void singletonOperation(){ } }
二:五种单例模式效率测试
Ps: CountDownLatch
同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。
CountDown()当前线程调用此方法,则计数减一(建议放在finally里执行)
Await(),调用此方法会一直阻塞当前线程,直到计时器的值为0
测试代码:
public class Test { public static void main(String[] args) throws Exception { long start = System.currentTimeMillis(); int threadNum = 10; CountDownLatch countDownLatch = new CountDownLatch(threadNum); for (int i = 0; i < threadNum; i++) { new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 10; i++) { Object o = SingletonDemo1.getInstance(); } countDownLatch.countDown(); } }).start(); } countDownLatch.await(); long end = System.currentTimeMillis(); System.out.println("总耗时:" + (end - start )); } }
测试结果:饿汉式<静态内部类<枚举式<双重检察琐式<懒汉式