单例模式:一个类中,有且仅有一个对象,并提供有且仅有的一个全局访问方法。
场景:
①数据库对象,I/O操作对象
②工具类对象
③全局共享的数据(Android中的MyApplication ,全局共享的数据等)
结构图如下:
Singleton |
- instance:Singleton |
- Singleton() + getInstance() |
【1】饿汉模式(一来就直接创建对象,无论是否使用)
public class Singleton{
public static Singleton singleton = new Singleton();
private Singleton(){
}
public static Singleton getInstance(){
return singleton;
}
}
特点:
①类加载时就完成了初始化(静态声明);
②基于类的加载机制,避免了多线程同步问题;
③类加载速度较慢,但获取对象的速度较快;
④如果一直没有使用,则会浪费资源。
■通俗理解:提前给了钱订了位置,但来不来用就是一码事,不来用就浪费资源
【2】懒汉模式(要用的时候才创建对象)
⑴线性不安全:
//线性不安全
public class Singleton{
private static Singleton singleton ;
private Singleton(){//此处将无参数的构造函数声明为私有,外部就无法重写,防止多个实例的产生
}
public static Singleton getInstance(){
if(singleton == null){
singleton = new Singleton();
}
return singleton;
}
}
特点:
①第一次调用才初始化,节省了资源;
②正是由于第一次调用才产生实例,所以第一次调用相对较慢,后续则较快;
⑵线性安全:
//线性安全
public class Singleton{
private static Singleton singleton = null;
private Singleton{
}
public static synchronized Singleton getInstance(){//注意此处使用了synchronized声明为同步
if(singleton == null){
singleton = new Singleton();
}
return singleton;
}
}
特点:
①第一次调用才初始化,节省了资源;
②正是由于第一次调用才产生实例,所以第一次调用相对较慢,后续则较快;
③由于使用了同步关键字,所以在多线程中是线性安全的;
④每次调用都需要同步一下,会造成一定的同步开销;
■通俗理解:上车买票,第一次上车时买了车票,以后每次到一个站上一次厕所回来再上车只是检查一下车票,但不用再买票
【3】双重检查锁定(double-checked locking:缩写DCL)模式(要用的时候才创建对象)
public class Singleton_dcl {
private volatile static Singleton_dcl singleton_dcl;
private Singleton_dcl(){
}
public static Singleton_dcl getInstance(){
if(singleton_dcl == null){//如果不为空,则跳过,避免每次都需要同步都消耗资源
synchronized (Singleton_dcl.class){
if(singleton_dcl == null){
singleton_dcl = new Singleton_dcl();
}
}
}
return singleton_dcl;
}
}
特点:
①第一次加载时才实例化单例对象,但第一次加载速度较慢
②避免每次调用都同步,省去多余的同步,节省资源消耗
③在高并发的情况下会有一定问题。
■通俗理解:上车买票,第一次上车时买票,会通过系统进行处理,后面每次停站从厕所回来,上车时乘务员就检查一下车票就行,不需要输入车票信息去查系统去同步查看
注意:双重检查锁定模式不适用1.4及更早版本的JAVA,因为许多JVM对于volatile的实现会导致该模式失效
【4】静态内部类单例模式
public class Singleton_innerCls {
private Singleton_innerCls(){}
private static class SingletonHolder{//内部类
private static final Singleton_innerCls mInstance = new Singleton_innerCls();
}
public static Singleton_innerCls getInstance(){
return SingletonHolder.mInstance;
}
}
特点:
①第一次加载类时不会初始化对象
②第一次调用getInstance()时,虚拟机加载静态内部类SingletonHolder,并且初始化对象
③可以确保线程安全
★总结:
①所有模式当中,有且仅有一个公共方法,就是获取对象的方法getInstance();
②其余的方法、常量、内部类统统都声明为私有private,特别要注意默认的构造方法必须要声明为私有