单例模式(八种写法)
描述
单例模式是常见的一种设计模式,使用单例模式的好处是节省内存和计算、保证结果正确、方便管理,最常用的有饿汉式和懒汉式两种,当然除了这两种还有另外几种方式,以下会详细介绍。
单例的实现思路为如下两步:
- 将该类的构造方法定义为私有方法,这样通过其它方法无法实例化该对象,只能通过该类提供的静态方法获取该类的唯一实例。
- 在该类提供一个静态方法,该方法持有该类的一个引用,当调用该方法时,如果引用不为空则直接返回,如果引用为空则创建实例并返回。
单例的使用场景:
- 无状态的工具类:比如日志工具类,不管是在哪里使用,我们需要的只是它帮我们记录日志信息,除此之外,并不需要在它的实例对象上存储任何状态,这时候我们就只需要一个实例对象即可。
- 全局信息类:比如我们在一个类上记录网站的访问次数,我们不希望有的访问被记录在对象A上,有的却记录在对象B上,这时候我们就让这个类成为单例。
单例模式的八种写法
1.饿汉式-静态常量 [可用]
class Singleton{
private final static Singleton singleton = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return singleton;
}
}
2.饿汉式-静态代码块 [可用]
class Singleton{
private static Singleton singleton;
static{
singleton = new Singleton();
}
private Singleton(){}
public static Singleton getInstance(){
return singleton;
}
}
3.懒汉式-非线程安全 [不可用]
class Singleton{
private static Singleton singleton;
private Singleton(){}
public static Singleton getInstance(){
if (singleton==null){
singleton = new Singleton();
}
return singleton;
}
}
4.懒汉式-线程安全 同步方法 [不推荐用]
class Singleton{
private static Singleton singleton;
private Singleton(){}
public static synchronized Singleton getInstance(){
if(singleton==null){
singleton = new Singleton();
}
return singleton;
}
}
5.懒汉式-线程不安全 同步代码块 [不可用]
class Singleton{
private static Singleton singleton = null;
private Singleton(){}
public static Singleton getInstance(){
if(singleton==null){
synchronized(Singleton.class) {
singleton = new Singleton();
}
}
return singleton;
}
}
6.双重检查机制 [推荐用]
为什么要用volatile
由于cpu与jvm多线程指令优化,会发生指令重排,volatile的特性就是可以保证多线程之间的可见性和禁止指令重排
如果不加volatile多线程环境下会发生NPE
class Singleton{
private static volatile Singleton singleton;
private Singleton(){}
public static Singleton getInstance(){
if(singleton==null){
synchronized(Singleton.class) {
if(singleton==null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
7.静态内部类 [推荐用]
class Singleton{
private Singleton(){}
public static class SingletonInstance{
private static final Singleton singleton = new Singleton();
}
public static Singleton getInstance(){
return SingletonInstance.singleton;
}
}
8.枚举 [推荐用]
enum Singleton{
INSTANCE;
public void whateverMethod(){
System.out.println("hello");
}
}
方法对比
- 最好的方法是利用枚举,因为还可以防止反序列化重新创建新的对象
- 非线程同步的方法不能使用
- 如果程序一开始要加载的资源太多,那么就应该使用懒加载
- 饿汉式如果是对象的创建需要配置文件就不适用
- 懒加载虽然好,但是静态內部类这种方式会引入编程复杂性