饿汉式单例模式
饿汉式单例模式分为两种:1、静态变量的方式 2、静态代码块的方式
1、静态变量的方式的实现
/**
* @author songle
* @create 2021-05-22 20:32
* @descreption 单例模式---饿汉式---静态变量
*/
public class HungerySingleton {
public static void main(String[] args) {
Singleton singleton1 = Singleton.getInstance();
Singleton singleton2 = Singleton.getInstance();
System.out.println(singleton1.equals(singleton2));
System.out.println(singleton1.hashCode());
System.out.println(singleton2.hashCode());
}
}
class Singleton {
// 1、构造器私有
private Singleton(){}
// 2、在本类的内部创建对象实例
private final static Singleton instance = new Singleton();
// 3、提供一个共有的静态方法,返回对象的实例
public static Singleton getInstance() {
return instance;
}
}
**
2、静态代码块的方式的实现
/**
* @author songle
* @create 2021-05-22 20:32
* @descreption 单例模式---饿汉式---静态代码块
*/
public class HungerySingleton2 {
public static void main(String[] args) {
Singleton2 singleton1 = Singleton2.getInstance();
Singleton2 singleton2 = Singleton2.getInstance();
System.out.println(singleton1.equals(singleton2));
System.out.println(singleton1.hashCode());
System.out.println(singleton2.hashCode());
}
}
class Singleton2 {
// 1、构造器私有
private Singleton2(){}
// 2、在本类的内部创建对象实例
private static Singleton2 instance = null;
// 3、静态代码块中实例化
static {
instance = new Singleton2();
}
// 3、提供一个共有的静态方法,返回对象的实例
public static Singleton2 getInstance() {
return instance;
}
}
**
饿汉式单例模式的总结
优点:这种方式比较简单,就是在类装载的时候就完成了实例化,避免了线程的同步问题。
缺点:在类装载的时候就完成了实例化,无法达到懒加载的效果,如果这个实例一直没有被使用,就会造成内存浪费。
这种方式基于classLoader机制避免了线程同步问题,不过instance在类装载的时候就完成了实例化,大多数都是直接调用getInstance方法,但是导致类装载的原因会有很多,因此不能确定有其他方式导致类装载,难以达到懒加载的效果。
结论:这种单例模式可用,但是可能会造成内存浪费。
懒汉式单例模式
懒汉式单例模式分为三种:1、线程不安全懒汉式 2、线程安全懒汉式
1、线程不安全懒汉式的实现
/**
* @author songle
* @create 2021-05-22 21:01
* @descreption 单例模式---懒汉式---线程不安全的实现方式
*/
public class UnSafeLazySingleton {
// 1、构造器私有
private UnSafeLazySingleton(){}
// 2、在本类的内部创建对象实例
private static UnSafeLazySingleton instance;
// 3、提供一个共有的静态方法,返回对象的实例,当需要用到该实例的时候才去创建
public static UnSafeLazySingleton getInstance() {
if (instance == null) {
instance = new UnSafeLazySingleton();
}
return instance;
}
public static void main(String[] args) {
UnSafeLazySingleton singleton1 = UnSafeLazySingleton.getInstance();
UnSafeLazySingleton singleton2 = UnSafeLazySingleton.getInstance();
System.out.println(singleton1.equals(singleton2));
System.out.println(singleton1.hashCode());
System.out.println(singleton2.hashCode());
}
}
总结
优点:起到了懒加载的效果,但是只能在单线程下使用。
如果在多线程下,一个线程进入了if (instance == null) 但是还没有生成实例的时候,另一个线程也执行到了这里,这时便会生成多个实例,所以在多线程的情况下不能使用这种方式。
结论:在实际开发中不要使用这种方式。
2、线程安全懒汉式的实现
/**
* @author songle
* @create 2021-05-22 21:01
* @descreption 单例模式---懒汉式---线程安全的实现方式
*/
public class SafeLazySingleton {
// 1、构造器私有
private SafeLazySingleton(){}
// 2、在本类的内部创建对象实例
private static SafeLazySingleton instance;
// 3、提供一个共有的静态方法,返回对象的实例,当需要用到该实例的时候才去创建
public static synchronized SafeLazySingleton getInstance() {
if (instance == null) {
instance = new SafeLazySingleton();
}
return instance;
}
public static void main(String[] args) {
SafeLazySingleton singleton1 = SafeLazySingleton.getInstance();
SafeLazySingleton singleton2 = SafeLazySingleton.getInstance();
System.out.println(singleton1.equals(singleton2));
System.out.println(singleton1.hashCode());
System.out.println(singleton2.hashCode());
}
}
总结
解决了线程不安全的问题。
每个线程想要获得类的实例的时候,在执行getInstance方法的时候都要进行同步。而实际上这个方法只需要执行一次实例化代码,后面的线程想要获得该类的实例直接return就可以。所以这种方法的效率很低。
结论:在实际开发中不推荐使用这种方式。
双重检查单例模式
/**
* @author songle
* @create 2021-05-22 21:01
* @descreption 单例模式---双重检查
*/
public class DoubleCheckSingleton {
// 1、构造器私有
private DoubleCheckSingleton(){}
// 2、在本类的内部创建对象实例
private static volatile DoubleCheckSingleton instance;
// 3、双重检查,解决线程安全问题,并且解决了懒加载的问题,同时保证了效率问题
public static synchronized DoubleCheckSingleton getInstance() {
if (instance == null) {
synchronized (DoubleCheckSingleton.class) {
if (instance == null) {
instance = new DoubleCheckSingleton();
}
}
}
return instance;
}
public static void main(String[] args) {
DoubleCheckSingleton singleton1 = DoubleCheckSingleton.getInstance();
DoubleCheckSingleton singleton2 = DoubleCheckSingleton.getInstance();
System.out.println(singleton1.equals(singleton2));
System.out.println(singleton1.hashCode());
System.out.println(singleton2.hashCode());
}
}
总结
解决了线程不安全的问题,同时解决了懒加载的问题。
实例化代码只会执行一次,下个线程进来就会就会直接return实例对象回去,提高了效率。
结论:在实际开发中推荐使用这种方式。
静态内部类单例模式
/**
* @author songle
* @create 2021-05-22 21:01
* @descreption 单例模式---静态内部类
*/
public class InnercClassSingleton {
// 1、构造器私有
private InnercClassSingleton(){}
// 2、写一个静态内部类,该类汇总有一个静态的属性
private static class SingletonInstance {
private static final InnercClassSingleton instance = new InnercClassSingleton();
}
// 3、直接返回类实例
public static InnercClassSingleton getInstance() {
return SingletonInstance.instance;
}
public static void main(String[] args) {
InnercClassSingleton singleton1 = InnercClassSingleton.getInstance();
InnercClassSingleton singleton2 = InnercClassSingleton.getInstance();
System.out.println(singleton1.equals(singleton2));
System.out.println(singleton1.hashCode());
System.out.println(singleton2.hashCode());
}
}
总结
这种方式采用了类装载的机制,保证了初始化的时候只有一个线程。
静态内部类在类装载的时候不会立刻实例化,而是在需要实例化的时候,调用getInstance方法,然后在内部类中完成实例化。
类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程的安全性,在类进行初始化的时候,别的线程是无法进入的。
优点:避免了线程不安全,利用静态内部类特点实现延迟加载,效率高。
结论:推荐使用。
枚举单例模式
/**
* @author songle
* @create 2021-05-22 21:01
* @descreption 单例模式---枚举
*/
public class EnumSingleton {
public static void main(String[] args) {
SingletonEnum singleton1 = SingletonEnum.INSTANCE;
SingletonEnum singleton2 = SingletonEnum.INSTANCE;
System.out.println(singleton1.equals(singleton2));
System.out.println(singleton1.hashCode());
System.out.println(singleton2.hashCode());
singleton1.sayOk();
}
}
enum SingletonEnum {
INSTANCE;
public void sayOk() {
System.out.println("ok~");
}
}
总结
不仅能避免多线程同步问题,还能防止反序列化重新创建新的对象。
结论:推荐使用。
单例模式在JDK源码中的应用
jdk的Runtime类就是使用了饿汉式的单例模式