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入门到精通教程
查看: 263|回复: 0

[默认分类] Java(Android)设计模式-单例模式

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

    [LV.4]偶尔看看III

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

    概述

      回顾一下之前写的单例模式文章,太过于繁琐,而且没有切中单例模式的要害,因此借机重写一下单例模式的具体实现方式。


      首先,单例模式一共有5种实现方式,分别是:


      
       饿汉式
       懒汉同步锁式
       懒汉双重校验锁式
       懒汉静态内部类式
       枚举类式
      
      
       

      


      我们以改造一个日志类为例,来介绍一下单例模式的5种实现。


      



      改造前的日志类源码如下:


    1. public class LogUtil {        public final int DEBUG = 0;
    2.         public int level = DEBUG;
    3.         public void debug(String msg) {
    4.                 if (DEBUG >= level) {
    5.                         System.out.println(msg);
    6.                 }
    7.         }
    8. }
    复制代码
      



      在介绍几种单例模式之前,我们以这个LogUtil类为例,说明一下为什么要使用单例模式?


      是因为单例模式可以做到:1. 减少内存开销,因为只有一个类实例; 2. 避免对资源的多重占用; 3. 实现了资源的全局访问.


      


    饿汉式

    1. public class LogUtil {    private static LogUtil sInstance = new LogUtil();    private LogUtil() {    }    public static LogUtil getInstance() {        return sInstance;    }    public final int DEBUG = 0;    private int level = DEBUG;    public void debug(String msg) {        if (DEBUG >= level) {            System.out.println(msg);        }    }    public static void main(String[] args) {        LogUtil logUtil = LogUtil.getInstance();        logUtil.debug("haha");    }}
    复制代码
      
       

      饿汉式是单例实现最简单的方式,因此它的优点也是实现简单,同样缺点也非常明显,做不到延迟加载。当单例类调用不是特别频繁且存在大量资源占用时,使用饿汉模式会导致单例类在程序初始时就被实例化,浪费系统资源。


      针对饿汉式这种存在资源浪费的缺点,因此单例又衍生了几种懒汉式的实现。


      


    懒汉同步锁式

      因为懒汉模式是延迟加载,需要考虑到线程安全,因此同步锁是使用synchronized对整个函数加Class锁来保证线程安全.


    1. public class LogUtil {    private static LogUtil sInstance;    private LogUtil() {    }    public synchronized static LogUtil getInstance() {        sInstance = new LogUtil();        return sInstance;    }    public final int DEBUG = 0;    private int level = DEBUG;    public void debug(String msg) {        if (DEBUG >= level) {            System.out.println(msg);        }    }    public static void main(String[] args) {        LogUtil logUtil = LogUtil.getInstance();        logUtil.debug("haha");    }}
    复制代码
    synchronized锁住整个函数,对多线程访问单例类时会造成排队等待的情况,归根节点的原因还是锁粒度太粗,因此又衍生出了懒汉双重校验锁式。


      



      懒汉双重校验锁式
      
       双重校验就是通过volatile关键字和减少synchronized的范围来减少多线程的竞争.
      
      
      
    1. public class LogUtil {    private volatile static LogUtil sInstance;    private LogUtil() {    }    public static LogUtil getInstance() {        if (sInstance == null) {            synchronized (LogUtil.class) {                if (sInstance == null) {                    sInstance = new LogUtil();                }            }        }        return sInstance;    }    public final int DEBUG = 0;    private int level = DEBUG;    public void debug(String msg) {        if (DEBUG >= level) {            System.out.println(msg);        }    }    public static void main(String[] args) {        LogUtil logUtil = LogUtil.getInstance();        logUtil.debug("haha");    }}
    复制代码
    双重校验锁在JDK1.5版本存在Bug,当然Android默认都是基于JDK1.6及以上版本编译,不存在该问题。为了兼容JDK1.5且简化代码实现,因此有衍生了静态内部类的懒汉式.
      
      
       

      
      
       懒汉静态内部类式
       
        因为是静态内部类,所以可以做到线程安全,且延迟加载。
       
       
       
    1. public class LogUtil {    private static class SingletonHolder {        public static LogUtil sInstance = new LogUtil();    }    private LogUtil() {    }    public static LogUtil getInstance() {        return SingletonHolder.sInstance;    }    public final int DEBUG = 0;    private int level = DEBUG;    public void debug(String msg) {        if (DEBUG >= level) {            System.out.println(msg);        }    }    public static void main(String[] args) {        LogUtil logUtil = LogUtil.getInstance();        logUtil.debug("haha");    }}
    复制代码
       
         

       
        枚举类式
       
         Effective java中推荐了一种更优雅的实现,就是使用枚举类来实现单例类。
       
       
         枚举除了线程安全和防止反射调用构造器外,还提供自动序列化机制,防止反序列化时自动创建新的对象.
       
       
       
    1. public enum LogUtil {    INSTANCE;    public final int DEBUG = 0;    private int level = DEBUG;    public void debug(String msg) {        if (DEBUG >= level) {            System.out.println(msg);        }    }    public static void main(String[] args) {        LogUtil logUtil = LogUtil.INSTANCE;        logUtil.debug("haha");    }}
    复制代码
         

       
       
      
      
    回复

    使用道具 举报

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

    本版积分规则

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

    GMT+8, 2024-6-15 22:47 , Processed in 0.356397 second(s), 37 queries .

    Powered by Discuz! X3.4

    © 2001-2017 Comsenz Inc.

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