TA的每日心情 | 开心 2021-12-13 21:45 |
---|
签到天数: 15 天 [LV.4]偶尔看看III
|
概述
回顾一下之前写的单例模式文章,太过于繁琐,而且没有切中单例模式的要害,因此借机重写一下单例模式的具体实现方式。
首先,单例模式一共有5种实现方式,分别是:
饿汉式
懒汉同步锁式
懒汉双重校验锁式
懒汉静态内部类式
枚举类式
我们以改造一个日志类为例,来介绍一下单例模式的5种实现。
改造前的日志类源码如下:
- public class LogUtil { public final int DEBUG = 0;
- public int level = DEBUG;
- public void debug(String msg) {
- if (DEBUG >= level) {
- System.out.println(msg);
- }
- }
- }
复制代码
在介绍几种单例模式之前,我们以这个LogUtil类为例,说明一下为什么要使用单例模式?
是因为单例模式可以做到:1. 减少内存开销,因为只有一个类实例; 2. 避免对资源的多重占用; 3. 实现了资源的全局访问.
饿汉式
- 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锁来保证线程安全.
- 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的范围来减少多线程的竞争.
- 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且简化代码实现,因此有衍生了静态内部类的懒汉式.
懒汉静态内部类式
因为是静态内部类,所以可以做到线程安全,且延迟加载。
- 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中推荐了一种更优雅的实现,就是使用枚举类来实现单例类。
枚举除了线程安全和防止反射调用构造器外,还提供自动序列化机制,防止反序列化时自动创建新的对象.
- 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"); }}
复制代码
|
|