单例模式也称单体模式,其目的是为了将类的责任集中到唯一的单体对象中,确保该类只有一个实例,并为该实例提供一个全局访问点
单例模式实现要点:
1.提供唯一私有化构造器,避免多个单体被创建
2.使用静态域来维护实例,将单例对象作为单例类的一个静态域实例化,并加以static修饰,需要时也可加final
3.使用静态方法来监视单例对象的创建
单例模式又分饿汉式与懒汉式:
饿汉式将对象初始化在静态变量中,每次返回该对象,以空间换时间,类只加载一次,对象自然也会初始化一次较为安全,但会是必须初始化,不管需不需要。(不常用)
懒汉式将对象初始化放在静态方法中,只有在调用时才会创建,此时会存在并发线程安全问题,当然对此会有优化和措施方案,具体实现代码如下:
一、饿汉式
/**
* 饿汉式单例,加载类时直接初始化对象
* 主要缺点在于一旦使用到该类的其他静态域,该对象会立即创建而不管需不需要,会耗费资源
*/
public class Singleton5 {
private static Singleton5 singleton5=new Singleton5();
private Singleton5(){}
public static Singleton5 getSingleton5(){
return singleton5;
}
}
二、基础懒汉式
**
* 原始标准单例模式
* 该单例模式在初次使用时创建,但在并发时会创建多个对象
*/
public class Singleton1 {
private static Singleton1 singleton1=null;
private Singleton1(){}
public static Singleton1 getSingleton1(){
if(null==singleton1) singleton1= new Singleton1();
return singleton1;
}
}
三、加锁懒汉式
**
* 针对singleton1在并发下的问题进行加锁
* 但其效率缓慢,浪费资源
*/
public class Singleton2 {
private static Singleton2 singleton2 =null;
private Singleton2(){}
public static synchronized Singleton2 getSingleton2(){
if(null==singleton2) singleton2=new Singleton2();
return singleton2;
}
}
四、双重加锁懒汉式
/**
* 针对Singleton2的资源浪费进行优化,只对对象的创建加锁并多判断一次空实例
* 双重检查加锁单例
* 该形式大多情况下没问题,但由于JVM的指令调优就会有很小的几率出错
* JVM创建对象步骤:
* 1.分配内存
* 2.初始化构造器
* 3,将内存地址交给变量指向
* 当JVM创建对象时3与2互换后便会出错:
* 线程A执行完3后线程B便会认为该实例已创建,在线程A未初始化完构造器之前线程B调用该对象便会出错
*/
public class Singleton3 {
private static Singleton3 singleton3=null;
private Singleton3(){}
public static Singleton3 singleton3(){
if(null==singleton3) {
synchronized (Singleton3.class) {
if(null==singleton3) //再次判断空实例原因:若不判断,AB线程都经过第一次判空A进入创建,A创建完成后退出B会继续创建
singleton3 = new Singleton3();
}
}
return singleton3;
}
}
五、内部类懒汉式
/**
*针对Singleton3可能会出现的问题再次优化
* 引入内部类,使用内部类静态变量定义对象,只初始化一次
* 与饿汉式的区别:
* 饿汉式会在加载类时直接初始化对象,而该方式使用内部类,内部类只有在被调用时才被加载,从而实现以懒汉式方式加载
*/
public class Singleton4 {
private Singleton4(){}
public static Singleton4 getSingleton4(){
return InstanceSingleton4.singleton4;
}
private static class InstanceSingleton4{
static Singleton4 singleton4=new Singleton4();
}
}
常见单例模式的应用场景:
任务管理器、回收站、网站计数器、数据库连接池、线程池
单例模式的自然推广:多例模式
这里的多例不是无限多个,而是在类中定义有限多个实例对象,向外部提供多个访问入口,如有多个数据库创建连接池时,单例模式就不适用了
public class DoubleSingleton {
private static DoubleSingleton doubleSingleton1=new DoubleSingleton();
private static DoubleSingleton doubleSingleton2=new DoubleSingleton();
private DoubleSingleton(){}
public static DoubleSingleton getDoubleSingleton(int i){
if(i==1) return doubleSingleton1;
else return doubleSingleton2;
}
}