设计模式包括三大类
创建型模式(主要用来建立对象)
单例模式 工厂模式 抽象工厂模式 建造者模式 原型模式
结构型模式
适配器模式 桥接模式 装饰模式 组合模式 外观模式 享元模式 代理模式
行为型模式
模板方法模式 命令模式 迭代器模式 观察者模式 中介者模式 备忘录模式 解释器模式 状态模式 策略模式 职责连模式 访问者模式
首先来说单例模式
核心作用:保证一个类只有一个实例,并且提供一个访问该实例的全局访问点
单例模式的常见应用场景:
Windows的任务管理器以及回收站
项目中,读取配置文件的类,一般也只有一个对象。没有必要每次使用配置文件数据,每次new一个对象去读取
网站的计数器,也是采用单例模式实现。否则难以同步。
应用程序的日志应用,一般都采用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加
数据库连接池的设计一般采用单例模式,因为数据库连接是一种数据库资源
操作系统的文件系统,也是大的单例模式实现的具体例子,一个操作系统只能有一个文件系统
Application也是典型的单例应用
Spring中每个bean默认是单例(singleton)的
在SpringMVC/Struts1框架中,控制器对象也是单例的
单例模式的优点:
由于单例模式只生成一个实例,减少了系统性能开支,当一个对象的产生需要比较多的资源时,如读取配置,产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻扎内存的方式来解决
单例模式可以在系统设置全局的访问点,优化和共享资源访问,例如可以设计一个单例类,负责所有数据表的映射处理
常见的五种单例模式及实现方式:
主要::
饿汉式:(线程安全,效率高,但是,不能延时加载)
懒汉式:(线程安全,效率相对不高,但是,可以延时加载)
其他:
双重检测锁式(由于JVM底层内部模型原因,偶尔会出现问题,不建议使用)
静态内部类式 (线程安全,效率高,可以延时加载)
枚举单例(线程安全,效率高,不能延时加载)
饿汉式实现:
1.构造方法私有
2.定义一个私有静态变量,并通过私有构造方法创建对象
3.定义一个public的静态方法,返回静态变量的值
public class SingletonHungry {
//构造方法私有化
private SingletonHungry (){}
//定义一个私有静态变量,获取私有构造方法 饿汉式 很饥饿 类初始化时立即加载对象(没有延时加载的优势)
//类加载的时候 是线程安全的 如果没有使用该类 但已经在初始化的时候加载了 浪费资源 饿汉式的缺点
private static SingletonHungry instance= new SingletonHungry ();
//定义一个public方法,返回静态变量的值 方法没有同步效率高
public static SingletonHungry getInstance(){
return instance;
}
}
饿汉式单例模式中,static变量会在类装载时初始化,此时也不会涉及多个线程对象访问该对象的问题。虚拟机保证只会装载一次该类,肯定不会发生并发访问的问题。因此可以省略synchronized关键字
类初始化的时候就会创建对象,如果未使用该对象 会造成资源的浪费 真实由于创建对象时机很快 所以叫饿汉式
调用效率很频繁的时候用饿汉式 类创建对象的成本很高的话用懒汉式
懒汉式(单例对象延迟加载)
实现:
1.构造方法私有化
2.定义一个私有静态变量
3.定义一个public的是static方法,在该方法被调用时再去创建对象并赋值给私有静态变量 并返回该私有变量
public class SingletonLazy {
//构造方法私有化
private SingletonLazy(){}
//定义一个私有静态变量,不初始化这个对象(延时加载,用的时候在创建)
private static SingletonLazy instance;
//定义一个public方法,在该方法被调用时再去创建对象并赋值给私有静态变量 并返回该变量 调用时才创建对象,所以需要加同步
public static synchronized SingletonLazy getInstance(){
if(null == instance){
instance= new SingletonLazy ();
}
return instance;
}
}
2.定义
要点:延迟加载,在真正需要的时候才加载
缺点:资源利用率高了。但是,每次调用getInstance()方法都要同步,并发效率低
为了综合弥补懒汉和饿汉的缺陷,讲一下另外 一些单例模式
双重检测锁实现
这个模式将同步内容下放到if内部,提高了执行的效率,不必每次获取对象时都进行同步(对懒汉式缺点的优化),只在第一次访问的时候才同步,以后访问就不用进行同步
代码实现:
public class SingletonDoubbleKey {
//构造方法私有化
private SingletonDoubbleKey (){}
//定义一个私有静态变量 volatile防止指令重排
private static volatile SingletonDoubbleKey instance;
public static SingletonDoubbleKey getInstance(){
if(null == instance){
synchronized(SingletonDoubbleKey.class){//将synchronized放到if里
if(null == instance){//双重检测
instance = new SingletonDoubbleKey();
}
}
}
return instance;
}
}
由于编译器优化原因和JVM底层内部模型原因,偶尔会出现问题,不建议使用。所以了解即可
静态内部类实现方式(也是一种懒加载方式)
实现代码:
public class SingletonDemo4 {
//构造方法私有化
private SingletonDemo4(){}
//定义私有静态内部类
private static class Singleton{
private static final SingletonDemo4 instance = new SingletonDemo4();
}
public static SingletonDemo4 getInstance(){
return Singleton.instance;
}
}
要点:
外部类没有static属性,不会像饿汉式那样立即加载对象
只有真正调用getInstance()方法,才会加载静态内部类。加载类时是线程安全的。instance是static final类型,保证了内存中只有这样一个实例存在,而且只能被赋值一次,从而保证了线程安全
兼备了并发高效调用和延迟加载的优势
使用枚举实现单例模式
代码实现:
public enum SingletonDemo5 {
//定义一个枚举的元素,它代表了一个singleton的一个实例
INSTANCE;
/**
* 单例可以有自己的操作
*/
public void operation(){
//功能处理
}
}
优点:
实现简单
枚举本身就是单例模式。由JVM从根本上提供保证,避免通过反射和反序列化的漏洞
缺点:
无延迟加载
总结:
单例模式实现
主要:
饿汉式(线程安全,调用效率高。但是,不能延迟加载)
懒汉式(线程安全,调用效率不高,同步方法需要等待,可以延迟加载)
其他:
双重检测锁式(由于JVM底层内部模型原因,偶尔会出现问题,不推荐使用)
静态内部类式(线程安全,调用效率高,而且可以延迟加载)
枚举式(线程安全,效率高,不可以延迟加载 可以天然的防止反射和反序列化漏洞)
如何选用?
单例对象占用资源少,不需要延迟加载时
枚举式好于饿汉式
单例对象占用资源大,需要延迟加载
静态内部类式好于懒汉式
原创不易:转载请标明出处:https://mp.csdn.net/postedit/80866450
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)