目录
(3)、双重校验锁DCL(double checked locking)
相关视频:
相关文章:
设计模式(创建型)之单例模式(Singleton Pattern)
相关面试题:
如何实现懒汉单例模式?口述代码实现。
手写一个单例算法的实现。
有几种常见的单例模式?对于这几种单例模式synchronized具体锁的是什么东西?
你能手写一个单例模式吗?分析一下它是怎样工作的。
你能熟练使用那些设计模式?并分析一下。
synchronized具体锁的是什么东西?(答案:被锁的代码块叫作临界区,只有获取到锁资源才能进入临界区,进行相应的操作)
一、概述:
单例模式(Singleton Pattern)是Java中最简单的的设计模式之一。这种类型的设计模式属于对象创建型模式。
作用:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
二、理解:
某些类创建对象是非常耗时耗内存和复杂的,这种类创建的对象我们称之为重量级对象(反之,轻量级对象)如果需要使用重量级类,一般把这个类设计为单例对象模式,好处有:
- 1、保证对象在内存中仅有一个,减少内存开销;
- 2、使用者不用考虑创建细节,使用方便;
- 3、可以控制对象的创建时刻;
1、饿汉式:
- 立即创建
- 线程安全
2、懒汉式:
- 延迟创建
- 线程不安全
3、加锁的懒汉式:
- 延迟加载
- 线程安全
- 同步情况下效率低
4、双重验证式懒汉式:
- 延迟加载
- 线程安全
- 同步情况下效率高
- 实现复杂
5、类加载方式:
- 延迟加载
- 线程安全
- 同步情况下效率高
- 实现简单
- 不能防止反序列化
6、枚举实现
- 立即加载
- 线程安全
- 实现简单
- 防止反序列化
三、所有单例模式
(1)、饿汉式:
上来就要用,很着急,跟饿死鬼似的,所以叫饿汉式
代码实例:
public class Singleton {
//创建唯一实例
private static Singleton mInstance = new Singleton();
//屏蔽外部的new
private Singleton() {
}
//提供一个全局的访问点
public static Singleton getInstance() {
//... 其他代码
return mInstance;
}
}
特点:
1、private构造函数,屏蔽外部的new
2、静态唯一实例
3、全局访问方法也是静态的
不足:
无法对instance实例做延时加载,因为该实例往往有很对数据需要初始化,需要延时加载
为什么叫饿汉式呢?
private static Singleton sInstance 这个实例是静态的变量,当Singleton这个类加载到虚拟机里面的时候,就会创建这个类
的实例,但是我们可能现在不需要,这就造成了内存的浪费,那有没有可能我们在使用的时候再去创建这个实例呢?也就是延迟创建呢?
(2)、懒汉式:
先声明,用的时候在实例化,所以叫懒汉式
public class Singleton {
//保存唯一实例
private static Singleton mInstance;
//屏蔽外部的new
private Singleton() {
}
//提供一个全局的访问点
public static Singleton getInstance() {
if (mInstance == null) {
mInstance = new Singleton();
}
//... 其他代码
return mInstance;
}
}
该种方式,如果在多线程情况下,就会不安全。例如,有多个线程调用getInstance()方法,第一次调用(例如Thread A调用)
的时候,因为sInstance为null,所以就会new Singleton();如果在new Singleton()方法之前,Thread B也调用了这个方法,可能
就会导致两次new Singleton(),这就导致了线程冲突,所以需要加同步锁,synchronized
以下为加锁的懒汉式:
public class Singleton {
//保存唯一实例
private static Singleton mInstance;
//屏蔽外部的new
private Singleton() {
}
//提供一个全局的访问点
public synchronized static Singleton getInstance() {
if (mInstance == null) {
mInstance = new Singleton();
}
//... 其他代码
return mInstance;
}
}
但是加锁的懒汉式的缺点就是效率低。该怎么优化呢? 其实我们只需要在
if (mInstance == null) {
mInstance = new Singleton();
}
这里加锁就可以了,后面的调用是不用加锁的。
(3)、双重校验锁DCL(double checked locking)
public class Singleton {
//保存唯一实例
private volatile static Singleton mInstance;
//屏蔽外部的new
private Singleton() {
}
//提供一个全局的访问点
public static Singleton getInstance() {
if (mInstance == null) {
synchronized (Singleton.class) {
if (mInstance == null) {
mInstance = new Singleton();
}
}
}
//... 其他代码
return mInstance;
}
}
缺点是实现有点复杂。
为什么要加上volatile 关键字呢?可以参考此文章:单例模式为什么要用Volatile关键字
实际案例ImageLoader:
private volatile static ImageLoader instance;
/** Returns singleton class instance */
public static ImageLoader getInstance() {
if (instance == null) {
synchronized (ImageLoader.class) {
if (instance == null) {
instance = new ImageLoader();
}
}
}
return instance;
}
protected ImageLoader() {
}
(4)、静态内部类实现(最推荐)
也叫类加载方式
public class Singleton {
//屏蔽外部的new
private Singleton() {
}
//静态内部类,用于持有唯一的Singleton的实例
private static class SingletonHolder {
private static final Singleton mInstance = new Singleton();
}
//公开的唯一访问点
public static Singleton getInstance() {
return SingletonHolder.mInstance;
}
}
(5)、枚举单例
public enum SingletonEnum {
INSTANCE;
public void method(){
// do something...
}
}
总的来说,加了volatile关键字的双重校验锁和静态内部类实现的单例模式是目前应用最为广泛的。
相关面试题:
四、Android中的单例
1、Application
2、单例模式引起的内存泄漏
3、Eventbus的坑