Java学习者论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

手机号码,快捷登录

恭喜Java学习者论坛(https://www.javaxxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,购买链接:点击进入购买VIP会员
JAVA高级面试进阶视频教程Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程

Go语言视频零基础入门到精通

Java架构师3期(课件+源码)

Java开发全终端实战租房项目视频教程

SpringBoot2.X入门到高级使用教程

大数据培训第六期全套视频教程

深度学习(CNN RNN GAN)算法原理

Java亿级流量电商系统视频教程

互联网架构师视频教程

年薪50万Spark2.0从入门到精通

年薪50万!人工智能学习路线教程

年薪50万!大数据从入门到精通学习路线年薪50万!机器学习入门到精通视频教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程 MySQL入门到精通教程
查看: 237|回复: 0

[默认分类] 设计模式——单例模式

[复制链接]
  • TA的每日心情
    开心
    2021-12-13 21:45
  • 签到天数: 15 天

    [LV.4]偶尔看看III

    发表于 2018-6-28 10:12:09 | 显示全部楼层 |阅读模式

    作为开发者的我们,想必设计模式对大家来说并不陌生,设计模式共有23种,分别是:工厂方法(FactoryMethod)、抽象工厂(AbstractFactory) 、建造者模式(Builder)、单态模式(Singleton)、 原型模式(Prototype)、 适配器模式(Adapter)、桥接模式(Bridge) 、组合模式(Composite) 、装饰模式(Decorator)、外观模式(Facade)、 享元模式(Flyweight)、代理模式(Proxy)、责任链模式(Chain of Responsibility)、 命令模式(Command)、 解释器模式(Interpreter) 、迭代器模式(Iterator)、中介者模式(Mediator)、备忘录模式(Memento)、观察者模式(Observer)、状态模式(State)、策略模式(Strategy) 、模板方法(TemplateMethod)、访问者模式(Visitor)。这23种设计模式又分为三类,分别是:创建型模式、结构型模式和行为型模式。创建型模式分别包含的是工厂方法(FactoryMethod)、抽象工厂(AbstractFactory) 、建造者模式(Builder)、单态模式(Singleton)、 原型模式(Prototype)。结构型模式分别包含的是: 适配器模式(Adapter)、桥接模式(Bridge) 、组合模式(Composite) 、装饰模式(Decorator)、外观模式(Facade)、 享元模式(Flyweight)、代理模式(Proxy)。行为型模式分别包含的是:责任链模式(Chain of Responsibility)、 命令模式(Command)、 解释器模式(Interpreter) 、迭代器模式(Iterator)、中介者模式(Mediator)、备忘录模式(Memento)、观察者模式(Observer)、状态模式(State)、策略模式(Strategy) 、模板方法(TemplateMethod)、访问者模式(Visitor)。
    了解了以上还远远不够,下面我们就来了解一下23种设计模式之一的单例模式。单例模式对我们开发者来说是再熟悉不过了。单例模式又分为饿汉式(线程安全,调用效率高。但是不能延时加载)、懒汉式(线程安全,调用效率低。可以延时加载)、双重检测锁(由于此模式偶尔会出现问题,不建议使用)、静态内部类式(线程安全,调用效率高。可以延时加载)、枚举单例(线程安全,调用效率高。不能延时加载)。单例模式的优点是:减少系统开销。我们经常使用的是饿汉式和懒汉式这两种。我们先来说一下饿汉式,饿这个字我想大家都知道啥意思,举个例子:当你很饿的时候你会去挑选你喜欢的食物吗?当然不会。当你很饿的时候,恰好发现有一些食物,此时的你不管是否是自己喜欢的食物,只要能充饥你都会去吃的。饿汉式模式也是一样,不论是否使用,先创建一个实例再说。缺点就是如果只加载本类,而不去调用getSingleton()这个方法的话,将会造成资源的浪费。因为一开始上来不论分说的先创建实例,这样很容易造成资源浪费。
    1. [code]public class Singleton {
    2.     private static Singleton singleton = new Singleton();
    3.     public Singleton() {
    4.     }
    5.     public static Singleton getSingleton() {
    6.         return singleton;
    7.     }
    8. }
    复制代码
    [/code]
    懒汉式则就不同了,懒汉式就好比你不是很饿,此时的你很困正在床上休息,不论旁边的人如何喊你吃东西,只要不是自己特别喜欢吃的,你都懒得起来去吃,当听到有你喜欢吃的东西的时候此时的你可能会去品尝一下。懒汉式就是这样,不调用就不实例化。虽然解决了资源的浪费,但是由于每次调用getSingleton() 时都要同步,造成并发效率特别低。也许有人会说我不会不加synchronized 这个关键字进行同步限制吗?但是这样做是不行的,如果并发量有点高的情况下容易造成多次实例化,比如说线程1和线程2调用了getSingleton()这个方法,当刚调用还没有来得及判断singleton是否为空是,此时线程1挂起了,然后当线程2执行到singleton = new Singleton2()时,线程2挂起,此时线程1又从刚刚挂起的地方执行,这样就导致被实例化了两次。如果加了synchronized 以后,即使线程1挂起,线程2也不会去执行,直到线程1执行完,线程2才会去执行。
    1. [code]public class Singleton2 {
    2.     private static Singleton2 singleton;
    3.     public Singleton2() {
    4.     }
    5.     public static synchronized Singleton2 getSingleton() {
    6.         if (singleton==null){
    7.             singleton = new Singleton2();
    8.         }
    9.         return singleton;
    10.     }
    11. }
    复制代码
    [/code]
    双重检测锁,顾名思义就是多次检测,这个模式将同步内容放到if里面,这样做提高了执行效率,没有必要每次获取对象时都要进行同步,只有第一次才会同步,以后每次都不需要。由于编译器优化问题此模式不建议使用。
    1. [code]public class Singleton3 {
    2.     private static Singleton3 singleton=null;
    3.     public Singleton3() {
    4.     }
    5.     public static Singleton3 getSingleton() {
    6.         if (singleton==null){
    7.             Singleton3 st;
    8.             synchronized (Singleton3.class){
    9.                 st = singleton;
    10.                 if (st==null){
    11.                     synchronized (Singleton3.class){
    12.                         if (st==null){
    13.                             st = new Singleton3();
    14.                         }
    15.                     }
    16.                     singleton = st;
    17.                 }
    18.             }
    19.         }
    20.         return singleton;
    21.     }
    22. }
    复制代码
    [/code]
    静态内部类模式,里面会用到final 这个关键字,static final 的作用就是防止被改变。这样获取到的空间地址永远是不会改变的,这样就会保证只有一个实例存在,而且是线程安全的。具备有高并发和延时加载的优势。
    1. [code]public class Singleton4 {
    2.     private static class SingletonDemo{
    3.         private static final Singleton4 singleton = new Singleton4();
    4.     }
    5.     public Singleton4() {
    6.     }
    7.     public static Singleton4 getSingleton() {
    8.         return SingletonDemo.singleton;
    9.     }
    10. }
    复制代码
    [/code]
    枚举单例,枚举本身就是单例,缺点就是没有延时加载。
    1. [code]public enum  Singleton5 {
    2.     SINGLETON;
    3. }
    复制代码
    [/code]
    基于以上五种单例模式我们应该如何选择呢?如果是单例对象,占用资源较少,而且又不需要延时加载的话枚举式好于饿汉式。如果是单例对象,占用资源较大,而且需要延时加载的话静态内部类好于懒汉式。单例模式就到此结束了,平时我们使用的Application里面就涉及有单例模式。

    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|Java学习者论坛 ( 声明:本站资料整理自互联网,用于Java学习者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2025-2-24 08:07 , Processed in 0.356747 second(s), 46 queries .

    Powered by Discuz! X3.4

    © 2001-2017 Comsenz Inc.

    快速回复 返回顶部 返回列表