分析懒汉式写法的特点:
由于调用getInstance时如果instance为null会创建对象 , 如果多个线程同时调用getInstance方法,有可能出现同步问题导致创建多个实例 , 所以getInstance方法使用了synchronized加锁来保障并发情况下也只会创建一个实例,不过synchronized的粒度较大,如果每次请求都经过getInstance方法,性能影响较大 。
4. 双检锁/双重校验锁(DCL , double-checked locking)懒汉式(线程安全)已经可以达到节省资源的目的,也达到了线程安全的目的 , 但是使用synchronized加锁对性能有较大影响 , 双检锁的方式,则是把锁的粒度尽可能降低,减少加锁对性能的影响 。
示例代码:
public class Singleton {private volatile static Singleton instance;private Singleton () {}public static Singleton getSingleton() {if (instance == null) {synchronized (Singleton.class) {if (instance == null) {instance = new Singleton();}}}return singleton;}}
分析双检锁的写法:
- 在成员属性instance上 , 我们增加了volatile关键字,保障多线程对instance值的可见性以及禁止指令重排 。
- 通过双重检查的方式,在内部再进行synchronized加锁,可以降低锁的粒度 , 有效避免每次调用getInstance都加锁,因为getInstance在创建对象之后,instance一直都是非null的 。
如果大家自行编写单例类,追求节约资源和高性能 , 可以使用这种写法,但据《Java并发编程实践》提到不赞成这个写法,推荐静态内部类的方式(这一点我尚未验证) 。
5. 静态内部类这个变种,可以达到和双检锁一样的效果,并且写起来更加简单,推荐使用 。
public class Singleton {private static class SingletonHolder {private static final Singleton INSTANCE = new Singleton();}private Singleton () {}public static final Singleton getInstance() {return SingletonHolder.INSTANCE;}}
分析一下静态内部类的特点:将instance放在了内部类SingletonHolder中,前面我们提到饿汉式是类加载时就会立即创建对象,而静态内部类不会,它只会在调用了getInstance时,才会加载内部类SingletonHolder,此时才会创建对象 。
6. 枚举这个方式,这里仅是从网上摘抄,据说是很好,但是没有试过,工作中也很少见 。
这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法 。
它更简洁,自动支持序列化机制 , 绝对防止多次实例化 。
这种方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题 , 而且还自动支持序列化机制,防止反序列化重新创建新的对象 , 绝对防止多次实例化 。
不过,由于 JDK1.5 之后才加入 enum 特性,用这种方式写不免让人感觉生疏,在实际工作中,也很少用 。
不能通过 reflection attack 来调用私有构造方法 。
public enum Singleton {INSTANCE;public void whateverMethod() {}}
7. 登记式如果熟悉我们封装的工具包Toolbox,就会知道工具包内提供了一个登记式单例工具类Singleton 。单例模式是一种非常常用的设计模式,但以上介绍的各种方法,都需要为每个单例类编写一些模板式的代码,为了简化,我们可以使用Singleton工具类 。
//获取单例对象//Student类必须要具备无参构造方法//每个类在一个进程中只能获得一个单例对象Student student = Singleton.get(Student.class);//移除单例对象Singleton.remove(Student.class);//清空所有单例对象Singleton.clear();//单例对象数量int size = Singleton.size();
其实他就是很像是spring容器 。Singleton.java:
/** * 单例工具 * @author Unicorn */public final class Singleton {/*** 对象池*/private static Map<String, Object> pool = new ConcurrentHashMap();private Singleton() {}public static <T> T get(Class<T> clazz) {Assert.notNull(clazz);String key = clazz.getName();T obj = (T) pool.get(key);if (null == obj) {synchronized(Singleton.class) {obj = (T) pool.get(key);if (null == obj) {obj = ReflectUtil.newInstance(clazz);pool.put(key, obj);}}}return obj;}/*** 移除对象* @param clazz*/public static void remove(Class clazz) {if (null != clazz) {String key = clazz.getName();pool.remove(key);}}/*** 销毁,清空对象池*/public static void clear() {pool.clear();}public static int size() {return pool.size();}}
8. Spring容器spring容器核心机制是IoC和DI,其本身也提供了单例对象的支持 。【Java单例模式,看这一篇就够了】
推荐阅读
- 微信支付v3接口的 官方 Java SDK
- Java函数式编程:二、高阶函数,闭包,函数组合以及柯里化
- 夯实Java基础,一篇文章全解析线程问题
- 小米mix fold pc模式_小米mix fold pc模式怎么打开
- 四 Java多线程-ThreadPool线程池-2
- 生成器函数 javascript异步编程之generator与asnyc/await语法糖
- Java Timer使用介绍
- 三 Java多线程-ThreadPool线程池
- 二 Java多线程-线程关键字
- 二 Java 编码那些事