最近在review代码的时候发现一个假单例模式, 从而想总结下单例模式。假单例模式代码如下:
1 public class RuleContext { 2 3 private static RuleContext ruleContext = new RuleContext(); 4 5 private ThreadLocal<List<Map<String, Object>>> ruleErrors = new ThreadLocal(); 6 7 public RuleContext() { 8 } 9 10 public static RuleContext getInst() { 11 return ruleContext; 12 } 13 }
从代码中可以看出类的构造函数(第七行)是public类型,不能保证只有一个实例。
一、单例模式定义
从上面假单例代码中可以看出,单例模式保证一个类仅有一个实例,并且提供一个全局访问点。有以下几个特点:
1、单例类只能有一个实例
2、单例类必须自己实例化
3、提供一个全局访问点
二、基本实现思路
1、将类的构造方法定义为私有方法,这样其它地方就不能够通个构造方法来实例化该类对象,只有通过该类的静态方法来获得该类的实例
2、在该类内部提供一个静态方法,当调用该方法时,判断类的实例是否为空,不为空则返回,为空则实例化对象
三、实现方式
1、懒汉式, 线程不安全
懒汉顾名思义:懒惰,当程序第一次访问单件模式实例时才进行创建。
1 /** 2 * <Description> <br> 3 * 4 * @author jy<br> 5 * @version 1.0<br> 6 * @taskId <br> 7 * @CreateDate 2019年04月01日 <br> 8 * @since R9.0<br> 9 * @see com <br> 10 */ 11 public class SingletonDemo { 12 // 利用静态变量来记录唯一实例 13 private static SingletonDemo singletonDemo; 14 15 // 私有构造方法 16 private SingletonDemo() { 17 18 } 19 20 // 在该类内部提供一个静态方法获取实例 21 public static SingletonDemo getInstance() { 22 if (singletonDemo == null) { 23 singletonDemo = new SingletonDemo(); 24 } 25 return singletonDemo; 26 } 27 }
此实现方式为线程不安全,当两个线程同时访问getInstance方法时会初始化不同的实例,原因在于方法没有加synchronized锁,严格意义上此实现方式不为单例模式
2、懒汉式,线程安全
这种实现方式只需要在前一种方式中获取实例方法上加锁,效率低下,但是做到延迟加载,节省内存,代码如下
1 public class SingletonDemo { 2 // 利用静态变量来记录唯一实例 3 private static SingletonDemo singletonDemo; 4 5 // 私有构造方法 6 private SingletonDemo() { 7 8 } 9 10 // 在该类内部提供一个静态方法获取实例,加锁保证保证单例,效率会低下,每次都会 11 public static synchronized SingletonDemo getInstance() { 12 if (singletonDemo == null) { 13 singletonDemo = new SingletonDemo(); 14 } 15 return singletonDemo; 16 } 17 }
3、饿汉式,线程安全
饿汉:类加载时就初始化,基于 classloader 机制避免了多线程的同步问题,不用加锁,效率高,不能够延迟加载,浪费内存
1 public class SingletonDemo2 { 2 // 利用静态变量来记录唯一实例 3 private static SingletonDemo2 singletonDemo = new SingletonDemo2(); 4 5 // 私有构造方法 6 private SingletonDemo2() { 7 8 } 9 10 // 在该类内部提供一个静态方法获取实例, 11 public static SingletonDemo2 getInstance() { 12 return singletonDemo; 13 } 14 }
4、双重检锁,线程安全
1 public class SingletonDemo1 { 2 /* 3 * 利用静态变量来记录SingletonDemo1的唯一实例 4 * volatile 关键字确保:当singleInstance变量被初始化成Singleton实例时, 5 * 多个线程正确地处理singleInstance变量,实际上让cpu指令禁止指令重排 6 */ 7 private volatile static SingletonDemo1 singleInstance; 8 9 private SingletonDemo1() { 10 11 } 12 13 public static SingletonDemo1 getSingleInstance() { 14 if (singleInstance == null) { 15 synchronized (SingletonDemo1.class) { 16 if (singleInstance == null) { 17 singleInstance = new SingletonDemo1(); 18 } 19 } 20 } 21 return singleInstance; 22 } 23 }
5、静态内部类,线程安全
1 public class SingletonDemo5 { 2 /** 3 * 静态内部类 4 */ 5 private static class SingletonHolder { 6 private static final SingletonDemo5 INSTANCE = new SingletonDemo5(); 7 } 8 9 // 私有构造 10 private SingletonDemo5() { 11 } 12 13 // 静态方法 14 public static final SingletonDemo5 getInstance() { 15 return SingletonHolder.INSTANCE; 16 } 17 }
6、枚举、线程安全
1 public enum Singleton6 { 2 INSTANCE; 3 public void whateverMethod() { 4 } 5 }