前言:单例模式在面试中,因为经常考到,而且还需要手写出来,所以,本次就为单例模式写一篇博客。算理下自己的思路。
1.概念
定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
全局只有这一个单一实例。
2.特点
- 将构造函数设为私有
这样外界就不能直接通过 new SingleTon() 来创建实例了。
- 创建一个私有的静态成员变量 instance
- 提供一个公共的静态方法 getInstance(),返回值为 instance
3.实现
分三步走写法:
1.包含自己的实例
2.构造方法私有
3.提供一个方法去给其他人获取实例
3.1 饿汉模式
饿汉模式通过两个静态修饰符修饰,一看就很饥渴,随着类的加载而加载,可以得到一个单一实例,线程是安全的。
public class Singleton {
private static Singleton instance =new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
}
但是可能会浪费空间,加载了很多没有用到的对象,因此想出了懒汉式单例。
3.2 线程不安全的懒加载
存在缺点:多线程下不能使用。所以我们提出了下面的方法--加锁
public class Singleton {
//1.包含自己的实例
private static Singleton singleton;
//2.将构造方法设为私有
private Singleton(){}
//3.提供一个方法去给其他人获取实例
public static Singleton getInstance(){
if(singleton==null){
singleton=new Singleton();
}
return singleton;
}
}
3.3 线程安全的懒加载
只加个锁就可以。
优点:多线程下依然是单例
缺点:加锁会影响执行效率。
但是每次调用 getInstance() 方法时都需要进行同步,造成不必要的同步开销,而且大部分时候我们是用不到同步的,所以不建议用这种模式。
public class Singleton {
private static Singleton singleton;
private Singleton(){}
public static synchronized Singleton getInstance(){
if(singleton==null){
singleton=new Singleton();
}
return singleton;
}
}
3.4 线程安全的立即加载
通过一个静态代码块实现这个效果
public class Singleton {
private Singleton(){}
private static Singleton singleton;
static {
singleton=new Singleton();
}
public static Singleton getInstance(){
return singleton;
}
}
也可以不用静态代码块
public class Singleton {
private Singleton(){}
private static Singleton singleton=new Singleton();
public static Singleton getInstance(){
return singleton;
}
}
【注意】:一旦一个类被载入JVM中,同一个类在一个进程就不会被再次载入了
在Java进程中,JVM只会在第一次调用某class类的时候把它加载进内存,之后再重复调用的时候,不再重复加载,而是直接从内存取出需要的变量/方法来使用。因此,静态变量、静态代码块···等也只会加载/执行一次。
3.5 线程安全的懒加载--静态内部类的实现(重要)
第一次加载该类时,并不会立即加载,当调用createInstance方法时,返回了一个静态内部类的方法。
这样不仅能确保线程安全也能保证Singleton类的唯一性,所以推荐使用静态内部类单例模式。
public class SingletonOutter {
//2、构造方法私有
private SingletonOutter(){}
static class Inner{
//1、提供自己的实例
static SingletonOutter singletonOutter = new SingletonOutter();
public static SingletonOutter createInnerInstance() {
return singletonOutter;
}
}
//提供一个方法给其他人调用 static
public static SingletonOutter createInstance(){
return Inner.createInnerInstance();
}
}
反射可以破解单例。但是可以加锁。反射不能破坏枚举的单例。
单例模式注意事项和细节说明
1) 单例模式保证了 系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使
用单例模式可以提高系统性能
2) 当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用 new
3) 单例模式 使用的场景:需要 频繁的进行创建和销毁的对象、创建对象时耗时过多或耗费资源过多(即:重量级
对象),但又经常用到的对象、 工具类对象、频繁访问数据库或文件的对象(比如 数据源、session 工厂等)
优秀博客