什么是单例模式?
有些对象我们只需要一个,比如配置文件、工具类、线程池、缓存、日志对象等,如果创造多个实例,就会导致许多问题,比如占用过多资源,不一致的结果等。
使用单例模式能够保证整个应用中有且只有一个实例。
单例模式的写法
1. 饿汉模式(推荐)
代码:
public class Singleton {
//1.将构造方法私有化,不允许外部直接创建对象
private Singleton(){
}
//2.创建类的唯一实例,使用private static修饰
private static Singleton instance=new Singleton();
//3.提供一个用于获取实例的方法,使用public static修饰
public static Singleton getInstance(){
return instance;
}
}
访问方式:Singleton instance = Singleton.getInstance();
饿汉模式的特点是加载类时比较慢,但运行时获取对象的速度比较快(初始化对象创建单例),因为是初始化就创建了实例,易造成资源浪费,线程安全。
2.懒汉模式(线程不安全,不推荐)
代码:
public class Singleton {
//1.将构造方式私有化,不允许外边直接创建对象
private Singleton(){
}
//2.声明类的唯一实例,使用private static修饰
private static Singleton instance;
//3.提供一个用于获取实例的方法,使用public static修饰
public static Singleton getInstance(){
if(instance==null){
instance=new Singleton();
}
return instance;
}
}
访问方式:Singleton instance = Singleton.getInstance();
懒汉模式的特点是加载类时比较快(加载类时new),但运行时获取对象的速度比较慢,线程不安全。既然是线程不安全的,那么就需要改进为线程安全形式的,经过大神们的探索,下面是其改进结果。
3.双重校验锁懒汉模式(推荐)
代码:
public class Singleton {
/**
* 懒汉式变种,属于懒汉式中最好的写法,保证了:延迟加载和线程安全
*/
private static Singleton instance=null;
private Singleton() {};
public static Singleton getInstance(){
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
访问方式:Singleton instance = Singleton.getInstance();
Double-Check概念对于多线程开发者来说不会陌生,如代码中所示,我们进行了两次if (instance== null)检查,这样就可以保证线程安全了。这样,实例化代码只用执行一次,后面再次访问时,判断if (instance== null),直接return实例化对象。
优点:线程安全;延迟加载;效率较高。
4.内部类方式(推荐)
代码:
public class Singleton{
private Singleton() {};
//内部类
private static class SingletonHolder{
private static Singleton instance=new Singleton();
}
public static Singleton getInstance(){
return SingletonHolder.instance;
}
}
访问方式:Singleton instance = Singleton.getInstance();
这种方式和饿汉式机制类似,两者都是采用了类装载的机制来保证初始化实例时只有一个线程。但是饿汉式是类被装载的时候就会实例化,没有lazy-loading的作用,而静态内部类方式在singleton类被装载时并不会立即实例化,而是需要实例化时,调用getInstance()方法,才会装载SingletonHolder类,从而完成Singleton的实例化。类的静态属性只会在第一次加载类的时候初始化,jvm保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。
优点:避免了线程不安全;延迟加载;效率高。
5.枚举方式(推荐)
代码:
public enum SingletonEnum {
instance;
private SingletonEnum() {}
public void method(){
}
}
访问方式:SingletonEnum.instance.method();
在这里SingletonEnum.instance这里的instance即为SingletonEnum类型的引用,所以得到它就可以调用枚举中的方法了。
借助JDK1.5中添加的枚举来实现单例模式。不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。