单例模式的介绍
单例模式是一种比较常见的设计模式,但是想要在Java中用好单例模式并不简单。在整个系统中,单例类只能有一个实例对象,且需自行完成实例化,并始终对外提供同一实例对象。
在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。
饿汉式的单例模式
public class Singleton {
//在类加载的时候创建单例实例,私有,只供内部调用
private static Singleton instance = new Singleton();
//创建私有无参构造
private Singleton(){}
//静态工厂方法
public static Singleton getInstance(){
return instance;
}
}
饿汉式的特点
1、名字来看就是类一旦加载,就把单例初始化完成,保证getInstance的时候,单例是已经存在的了
2、饿汉式天生就是属于线程安全的,可以直接用于多线程而不会出现线程安全的问题
3、因为饿汉式在开始类创建的时候就实例化一个静态的对象出来,对象在内存中,会占用一定的内存空间。
所以当你第一次调用的时候速度会非常的快,但是如果你不使用,他也会在内存中,比较浪费内存的空间
懒汉式的单例模式
public class Singleton {
private Singleton(){}
private static Singleton instance = null ;
//静态方法工厂
public static Singleton getInstance(){
//只有在第一次真正需要的时候才会去创建
if (instance == null){
instance = new Singleton();
}
//返回单例对象
return instance;
}
}
懒汉模式会在真正使用单例对象时才创建单例对象。但在多线程场景中,它是线程不安全的,并发环境下很可能出现多个Singleton实例,这显然不符合要求。以下都是对getInstance这个方法改造,保证了懒汉式单例的线程安全问题
在方法上加锁
public class Singleton {
private Singleton(){}
//只有第一次调用的时候才去加载
private static Singleton1 instance = null ;
//在方法上加上synchronized
public static synchronized Singleton getInstance(){
if (instance == null){
instance = new Singleton();
}
//返回单例对象
return instance;
}
}
此种锁虽然保证了线程安全,但是每次都要去同步,会影响性能问题
双重校验锁
public class Singleton {
private Singleton(){}
//使用volatile关键字防止重排序,因为 new Instance()是一个非原子操作,可能创建一个不完整的实例
private static volatile Singleton instance = null ;
//静态方法工厂
public static Singleton getInstance(){
//避免每次加锁,只有在第一次没有创建对象的时候才会加锁
if (instance == null){
//加锁,只允许一个线程进入
synchronized (Singleton.class){
//只创建一次对象
if (instance == null){
instance = new Singleton();
}
}
}
//返回单例对象
return instance;
}
}
在getInstance中做了两次null检查,确保了只有第一次调用单例的时候才会做同步,
这样也是线程安全的,同时避免了每次都同步的性能损耗
私有静态内部类
// 私有静态内部类
public class Singleton {
// 私有构造方法
private Singleton(){}
// 私有静态内部类
private static class SingletonHolder {
private static final Singleton instance = new Singleton();
}
public static Singleton getInstance(){
// 返回静态内部类中的静态字段
return SingletonHolder.instance;
}
}
既实现了线程安全,又避免了同步带来的性能影响