单例模式的定义:确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
说白了就是,保证一个类仅有一个实例,并提供一个访问它的全局访问点。
从定义中,我们可以看出:
- 单例类只能有一个实例。
- 单例类必须自行创建自己的唯一的实例。
- 单例类必须给所有其他对象提供这一实例 ,向整个系统提供这个实例。
饿汉单例:
/**
* 饿汉
*/
public class HungrySingleton {
// 顾名思义:饿汉就是一开始就直接实例化
private static HungrySingleton sInstance = new HungrySingleton();
//私有化空构造方法
// 不允许外部实例化
private HungrySingleton() {
// TODO: init something,if needed!
}
//静态方法返回单例类对象
public static HungrySingleton getInstance() {
return sInstance;
}
}
饿汉模式是线程安全的
,因为类加载时就会初始化单例对象,并且只初始化一次
。
ClassLoader的loadClass方法在加载类的时候使用了synchronized关键字。
饿汉式单例虽然在首次调用的时候快
,但是类加载时就会初始化单例对象,容易产生垃圾
。
可以与java.lang.Runtime
对照着看:
懒汉模式:
/**
* 懒汉:首次调用的时候才会进行实例化
*/
public class LazySingleton {
private static LazySingleton sInstance;
//私有化空构造方法
private LazySingleton() {
}
//静态方法返回单例类对象
public static LazySingleton getInstance() {
//懒加载
if (sInstance == null) {
sInstance = new LazySingleton();
}
return sInstance;
}
}
PS: 线程不安全,因为getInstance()方法没有做任何的同步处理。
因为是首次调用的时候才会实例化,所以相比饿汉式单例会比较节省资源。
由于线程不安全,所以我们可能就会想着加锁,可是加锁后面临的问题就是每次外部调用getInstance()时效率会明显下降。
像下边这样:
// 静态方法属于类,给静态方法加锁相当于把整个类都锁住了
public synchronized static LazySingleton getInstance() {
//懒加载
if (sInstance == null) {
sInstance = new LazySingleton();
}
return sInstance;
}
这就成线程安全的懒汉模式了。(一般不会这么做的)
双检锁(DCL:Double Check Lock):
/**
* @author yuan
* @version 1.0.0
* @Description DCL
* @date 2018/10/4 17:11
*/
public class DCLSingleTon {
private static DCLSingleTon sInstance;
private DCLSingleTon() {
}
public static DCLSingleTon getInstance() {
// first check
if (null == sInstance) {
synchronized (DCLSingleTon.class) {
// second check
if (null == sInstance) {
sInstance = new DCLSingleTon();
}
}
}
return sInstance;
}
}
DCL:线程安全。相比加锁后的懒汉式单例更加有效率。
刚开始我还在想:为什么要判空两次呢? 第一次判空后,如果为null就已经加锁了呀!后来想了想确实两次判空是有道理的,假如有这种情况:
线程A和线程B同时进行了第一次判空,都是null那么自然就顺利进入代码块中,然而此时由于加锁原因线程排队阻塞,等待其中一个线程执行完后,另外一个进入synchronized块中执行,此时如果没有第二次判空,那么就会重复实例化,线程就不安全了。而二次判空就可以完美解决这个问题。
静态内部类单例:
/**
* @author yuan
* @version 1.0.0
* @Description 静态内部类单例模式
* @date 2018/10/4 17:30
*/
public class StaticInnerClassSingleton {
private static StaticInnerClassSingleton sInstance;
private StaticInnerClassSingleton() {
}
public static StaticInnerClassSingleton getInstance() {
// 静态内部类只有在外部有人使用的时候才会进行加载,并不是和外部类一起加载的。(懒加载)
return SingleClass.STATICINNERCLASSSINGLETON;
}
//静态内部类
private static class SingleClass {
private static final StaticInnerClassSingleton STATICINNERCLASSSINGLETON = new StaticInnerClassSingleton();
}
}
静态内部类只有在外部有人使用的时候才会进行加载,并不是和外部类一起加载的。(懒加载)
静态内部类中的静态变量是通过类加载器初始化的,所以线程安全。