1、饿汉式
优点:类加载到内存后,就实例化一个单例,JVM保证线程安全。
缺点:不论是否使用,类装载时就完成了实例化。(话说你要不用,你写它干嘛???所以此缺点微乎其微)
最简单使用,完成一般业务足够~
package com.example.demo.test;
public class Singleton {
//初始化静态的对象
private static final Singleton INSTANCE = new Singleton();
//私有化对象的构造方法(其他地方无法new出此对象,必须调用此对象提供的公有的方法得到静态对象)
private Singleton(){}
//提供公有的获取静态对象的方法
public static Singleton getInstance(){
return INSTANCE;
}
2、懒汉式
优点:解决了上面的缺点,用到的时候再实例化
缺点:带来了线程的不安全
package com.example.demo.test;
public class Singleton {
private static Singleton instance;
private Singleton(){}
public static Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
使用双重检查解决线程不安全问题:
package com.example.demo;
public class Singleton {
/**
* volatile关键字必须,否则指令重排时会出问题!!!
* 当一个线程获得锁new Singleton()的时候,指令有可能重排,
* 即:先将内存给到instance,再进行初始化。
* 这个时候当第二个线程进来,进入第一步判断时,instance != null
* 直接返回了一个半初始化状态的对象,会有问题
*/
private static volatile Singleton instance;
private Singleton(){
}
public static Singleton getInstance(){
if(instance == null){
synchronized (Singleton.class){
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
3、完美的方式:使用静态内部类
package com.example.demo.test;
public class Singleton {
private Singleton(){}
//静态内部类,在调用getInstance的时候才加载,解决了饿汉的缺点,同时JVM保证线程安全
private static class Inner{
private static final Singleton instance = new Singleton();
}
public static Singleton getInstance(){
return Inner.instance;
}
4、最最最完美的方式:枚举单例
不仅能够解决线程同步,还能防止反序列化(因为没构造方法)。
package com.example.demo.test;
public enum EnumSingleton {
INSTANCE;
}