一、创建型设计模式的作用
关注点在与怎样创建对象,将创建对象和使用对象分离
二、核心思想:
单例模式的核心在于要保证内存里面始终只存在一个类对象。
主要从2个方面考虑:
1、必须私有化构造方法,防止在外部new对象
2、必须提供一个公有方法给外部调用,这个公有方法里面实现单例创建对象的逻辑
三、实现方式
1、利用类加载机制(急加载)
(1)、思想
利用类加载机制,在类加载的时候,被static修饰的成员变量就会加载到内存中,
并且在是类对象可以保证内存中只有一个。
缺点在于在类加载的时候就会初始化对象,有些时候并不需要进行急加载,
而是等到需要的时候再初始化。
(2)、步骤
1. 私有化构造方法
2. 声明static成员变量,在类加载的时候就new出对象
3. 声明一个公有方法,直接返回这个static成员变量指向的对象
(3)、代码
//static急加载,利用类加载机制,实现单例
public class Singleton1 {
//1、私有化构造方法,让外部不能new出对象
private void singleton(){};
//2、声明一个static修饰的私有对象,在类加载的时候,就实例化出来。
private static Singleton1 SINGLETON= new Singleton1();
//3、声明一个公有方法,用于返回实例化出来的单例对象
public static Singleton1 getSingeton(){
return SINGLETON;
}
}
2、利用类加载机制(懒加载)
(1)、思想
和利用类加载机制(急加载)实现方式的区别在于,不用一加载类就初始化对象,
可以等到需要(公有方法被调用的时候)再初始化单例对象。
在多线程的情况下,可能会破坏单例。
当一个线程在if判断变量为null时挂起,另外一个线程进行判断,
同样会判断变量为null,这个时候两个线程都进入了非空判断逻辑,所以迟早会破坏单例。
(2)、步骤
1. 私有化构造方法
2. 声明static修饰的成员变量,先不初始化
3. 声明公有方法,在方法里面先判断static成员变量是否为null,如果为null则初始化,
如果不为null则直接返回。
(3)、代码
//static懒加载,利用类加载机制,实现单例,
// 多线程操作可能会有问题
// 因为可能第一个线程进入了if判断,
// 然后第二个线程开始执行,第二个线程还是会判断为null,抢占到锁之后进行new对象操作,这样就保证不了单例。
public class Singleton2 {
//1、私有化构造方法,让外部不能new出对象
private void singleton2(){};
//2、声明static成员变量,类加载的时候先不实例化
private static Singleton2 SINGLETON;
//3、静态方法,判断static成员变量是否为null,如果为null初始化
public static Singleton2 getSingeton(){
if (SINGLETON==null){
SINGLETON=new Singleton2();
}
return SINGLETON;
}
}
3、懒汉式加锁
(1)、思想
在懒汉式的基础上,在if判断static变量为null的代码里面
加上synchronized锁。
缺点:
在多线程的情况下,也可能会破坏单例。
当一个线程在if判断变量为null时挂起,另外一个线程进行判断,
同样会判断变量为null,这个时候两个线程都进入了非空判断逻辑,
就算加锁也无法阻止,因为抢占到锁之后,还是都会执行new对象的操作。
(2)、步骤
1. 私有化构造方法 声明static修饰的成员变量,先不初始化
2. 声明公有方法,在方法里面先判断static成员变量是否为null,
3. 如果不为null则直接返回。如果为null则加锁初始化
(3)、代码
//懒汉式加锁实现,在公有方法里面if判断是对象否为null,如果为null就加锁new对象,
// 这样还是有风险,
// 因为可能第一个线程进入了if判断,
// 然后第二个线程开始执行,第二个线程还是会判断为null,抢占到锁之后进行new对象操作,这样就保证不了单例。
public class Singleton3 {
//1、私有化构造方法,让外部不能new出对象
private void singleton3(){};
//2、类加载时,初始化为null
private static Singleton3 singleton3=null;
//3、公共方法
public static Singleton3 getSingleton(){
//3、如果对象为null,就加锁初始化对象
if (singleton3==null){
synchronized (Singleton3.class){
singleton3=new Singleton3();
}
}
return singleton3;
}
}
4、双重加锁
(1)、思想
在懒汉式加锁的基础上,再加一层锁。先判断null,如果对象为null就加锁,
在加锁代码里面第二次判断对象是否为null,如果为null再进行new对象,
这样就可以保证多线程下的单例。
因为就算因为线程挂起的原因,多个线程进入了第一层为null判断,
这个时候通过synchronized可以保证只有一个线程进入加锁逻辑,
等这个线程执行完成释放锁之后,
其它线程就算获取锁进入之后也通不过第二次的if为null判断。
缺点在于代码逻辑复杂
(2)、步骤
1. 私有化构造方法 声明static修饰的成员变量,先不初始化
2. 声明公有方法,在方法里面先第一次判断static成员变量是否为null,
如果不为null则直接返回。
3. 如果为null则加锁,在加锁逻辑里面第二次判断变量是否为null,
如果还是为null则进行new操作。
(3)、代码
//双重检查,第一个if判断,如果为null,就加锁处理
//在synchronized里面再进行第二次if判断,这样就可以防止第一次判断完成之后,
//还未加锁之前,其它线程进入的情况,导致的不能保证单例。
//第一次if判断也很有必要,因为大多少情况下,并不需要执行到加锁代码,就可以直接返回。
public class Singleton4 {
//1、私有化构造方法,让外部不能new出对象
private void singleton4(){};
//2、类加载时,初始化为null
private static Singleton4 singleton4=null;
//3、公共方法
public static Singleton4 getSingleton(){
//3、如果对象为null,就加锁初始化对象
if (singleton4==null){
synchronized (Singleton4.class){
if (singleton4==null){
singleton4=new Singleton4();
}
}
}
return singleton4;
}
}
5、静态内部类实现
(1)、思想
利用静态内部类,在外部类被加载的时候不会加载内部类的特性,实现懒加载。
利用静态内部类之后被加载一次,内部类里面的成员变量只会被初始化一次的特性,
实现线程安全。
缺点在于会生成一个内部类。
(2)、步骤
1. 私有化构造方法 。
2. 声明一个静态内部类,内部类里面声明一个静态成员变量,new对象指向这个变量。
3. 声明一个公有方法,方法里面返回静态内部类里面的静态成员变量。
(3)、代码
//利用静态内部类在外部类加载的时候,不会初始化的特性,保证懒加载
// 通过公有方法,返回内部类的静态变量,在加载静态内部类的时候可以保证单例。
public class Singleton5 {
//1、私有化构造方法,让外部不能new出对象
private void singleton5(){};
//2、声明静态内部类,在内部类里面实例化对象
private static class innerSingleton {
private static Singleton5 singleton5=new Singleton5();
}
//3、公共方法,返回内部类的静态变量
public static Singleton5 getSingleton(){
return innerSingleton.singleton5;
}
}
6、枚举方式实现
(1)、思想
因为枚举类编译之后,实际上是一个继承了Enum<?>接口的常量类,
每个枚举值编译之后实际上是一个静态常量类对象。
所以根据静态常量类这些特性,就可以保证单例和线程安全。
并且枚举编译之后,是没有构造方法的,这就防止了外部利用反射来生成这个类对象破坏单例
而且在实现对象序列化的时候,也对枚举类做了限制,会抛出异常,
所以也防止了外部通过序列化方式来破坏单例。
缺点在于这种方式虽然是最完美的,但是违反常识,不便理解。
(2)、步骤
1. 声明一个枚举类
2. 声明一个枚举值
3. 在客户端调用这个枚举值获取单例对象。
(3)、代码
//通过枚举方式,枚举类编译之后是常量类 public final Singleton6 extends Enum<Singleton6>
//枚举值编译之后是常量public static final Singleton6 INSTANCE
//所以可以保证单例和线程安全,并且因为枚举类没有构造方法,
//还可以防止调过反射生成对象,有校验机制,可以防止序列化。
public enum Singleton6 {
INSTANCE;
}