提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 前言
- 一、为什么要用单例模式
- 二、单例模式的六种实现
-
- [2.1 饿汉式](#2.1 饿汉式)
-
- [2.1.1 饿汉式代码实现](#2.1.1 饿汉式代码实现)
- [2.1.2 饿汉式代码实现要点解析](#2.1.2 饿汉式代码实现要点解析)
- [2.1.3 饿汉式代码实现优点](#2.1.3 饿汉式代码实现优点)
- [2.1.4 饿汉式代码实现缺点](#2.1.4 饿汉式代码实现缺点)
- [2.2 懒汉式1.0](#2.2 懒汉式1.0)
-
- [2.2.1 懒汉式1.0代码实现](#2.2.1 懒汉式1.0代码实现)
- [2.2.2 懒汉式1.0代码实现要点解析](#2.2.2 懒汉式1.0代码实现要点解析)
- [2.2.3 懒汉式1.0代码实现优点](#2.2.3 懒汉式1.0代码实现优点)
- [2.2.4 懒汉式1.0代码实现缺点](#2.2.4 懒汉式1.0代码实现缺点)
- [2.3 懒汉式2.0](#2.3 懒汉式2.0)
-
- [2.3.1 懒汉式2.0代码实现](#2.3.1 懒汉式2.0代码实现)
- [2.3.2 懒汉式2.0代码实现要点解析](#2.3.2 懒汉式2.0代码实现要点解析)
- [2.3.3 懒汉式2.0代码实现优点](#2.3.3 懒汉式2.0代码实现优点)
- [2.3.4 懒汉式2.0代码实现缺点](#2.3.4 懒汉式2.0代码实现缺点)
- [2.4 懒汉式3.0------双重检查Double Check](#2.4 懒汉式3.0——双重检查Double Check)
-
- [2.4.1 双重检查代码实现](#2.4.1 双重检查代码实现)
- [2.4.2 双重检查代码实现要点解析](#2.4.2 双重检查代码实现要点解析)
- [2.4.3 双重检查代码实现优点](#2.4.3 双重检查代码实现优点)
- [2.4.4 双重检查代码实现缺点](#2.4.4 双重检查代码实现缺点)
- [2.5 静态内部类(推荐)](#2.5 静态内部类(推荐))
-
- [2.5.1 静态内部类代码实现](#2.5.1 静态内部类代码实现)
- [2.5.2 静态内部类代码实现要点解析](#2.5.2 静态内部类代码实现要点解析)
- [2.5.3 静态内部类代码实现优点](#2.5.3 静态内部类代码实现优点)
- [2.5.4 静态内部类代码实现缺点](#2.5.4 静态内部类代码实现缺点)
- [2.5.5 静态内部类与饿汉式对比](#2.5.5 静态内部类与饿汉式对比)
- [2.6 枚举(推荐)](#2.6 枚举(推荐))
-
- [2.6.1 枚举代码实现](#2.6.1 枚举代码实现)
- [2.6.2 枚举代码实现要点解析](#2.6.2 枚举代码实现要点解析)
- [2.6.3 枚举实现优点](#2.6.3 枚举实现优点)
前言
由于设计模式在面向对象中起着举足轻重的作用,在面试中很多公司都喜欢问一下有关设计模式的问题。在常用的设计模式中,Singleton单例模式是唯一一个能用短短几十行代码完整实现的模式,因此,写一个Singletion类型是一个很常见的面试题。
一、为什么要用单例模式
节省内存,单例对象可避免频繁的创建与销毁,带来性能的提升。
在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。
二、单例模式的六种实现
2.1 饿汉式
2.1.1 饿汉式代码实现
java
public class Singleton{
private Singleton(){}
private static Singleton instance = new Singleton();
public static Singleton getInstance(){
return instance;
}
}
2.1.2 饿汉式代码实现要点解析
1、私有构造
2、创建私有静态实例
3、提供公有静态get方法返回该静态实例
2.1.3 饿汉式代码实现优点
1、实现简单
2、无线程安全问题
2.1.4 饿汉式代码实现缺点
1、初始化耗时,导致系统启动缓慢
2、在类装载的时候就完成了实例化,没有做到Lazy Loading(延迟加载)、按需加载
3、极端情况下,还是可以拿到多实例,如通过反射。
2.2 懒汉式1.0
2.2.1 懒汉式1.0代码实现
java
public class Singleton{
private Singleton(){}
private static Singleton instance = null;
public static Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
2.2.2 懒汉式1.0代码实现要点解析
1、私有构造
2、初始化一个为null的静态实例instance
3、提供公有静态get方法,判断instance为null时,则创建该对象并赋给instance,然后返回该静态实例
2.2.3 懒汉式1.0代码实现优点
1、延迟加载,在使用时才会开辟空间
2.2.4 懒汉式1.0代码实现缺点
1、线程不安全,只能在单线程情况下使用,在多线程情况下,一个线程进if(instance == null)判断中,还未来得及创建对象,另外一个线程也来到判断语句,这时也会通过判断,这样就会创建多个实例。
总的来说在多线程环境中不能使用这种方式。
2.3 懒汉式2.0
2.3.1 懒汉式2.0代码实现
java
public class Singleton{
private Singleton(){}
private static Singleton instance = null;
public static synchronized Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
2.3.2 懒汉式2.0代码实现要点解析
1、私有构造
2、创建私有静态实例
3、提供加synchronized的公有静态get方法返回该静态实例
2.3.3 懒汉式2.0代码实现优点
1、用的时候开辟内存
2、解决线程安全问题
2.3.4 懒汉式2.0代码实现缺点
1、效率太低。下一个线程想要获取对象,必须等待上一个线程释放锁之后才能获取。每次调用都需要获取锁与释放锁,在大量并发请求时将产生性能问题。
2、并发度低。由于加入了synchronized,并行度为1,导致并发度低。
3、极端情况下,还是可以拿到多实例,如通过反射。
2.4 懒汉式3.0------双重检查Double Check
2.4.1 双重检查代码实现
java
public class Singleton{
private Singleton(){}
private static Singleton instance = null;
public static synchronized Singleton getInstance(){
if(instance == null){//这个检查是提高效率的
synchronized (Singleton.class){
if(instance == null){//这个检查是保证线程安全的
instance = new Singleton();
}
}
}
return instance;
}
}
2.4.2 双重检查代码实现要点解析
1、私有构造
2、创建私有静态实例
3、提供加synchronized的公有静态get方法返回该静态实例,该get方法中首先判断instance是否为null,然后加个synchronized块,里面再对instance进行判断,为空才创建对象
2.4.3 双重检查代码实现优点
1、实现简单
2、立即加载,无线程安全问题
2.4.4 双重检查代码实现缺点
极端情况下,还是可以拿到多实例,如通过反射。
2.5 静态内部类(推荐)
2.5.1 静态内部类代码实现
java
public class Singleton{
private Singleton(){}
private static Singleton SingletonInstance {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance(){
return SingletonInstance.INSTANCE;
}
}
2.5.2 静态内部类代码实现要点解析
1、私有构造
2、创建私有静态内部类,该静态内部类中含有static 和final修饰的实例对象
3、提供公有静态get方法返回该静态实例
2.5.3 静态内部类代码实现优点
1、延迟加载,效率高
2、无线程安全问题。类的静态属性只会在第一次加载类时初始化,这样JVM帮助我们保证了线程安全性,因为在类初始化时别的线程无法进入。
2.5.4 静态内部类代码实现缺点
静态内部类虽然保证了单例在多线程并发下的线程安全性,但是在遇到序列化对象时,默认的方式运行得到的结果就是多例的。
极端情况下,还是可以拿到多实例,如通过反射。
2.5.5 静态内部类与饿汉式对比
此方法可以看作饿汉式的改进版,两者都采用类装载机制来保证初始化实例时只有一个线程。
不同之处在于饿汉式只要Singleton类被装载就会实例化,没有延迟加载;
而静态内部类方式再Singleton类被装载时并不会立即实例化,而是在需要实例化时才会调用getInstance方法装载SingletonInstance 类,从而完成Singleton实例化。
总的来说推荐使用。
2.6 枚举(推荐)
2.6.1 枚举代码实现
java
public class Singleton{
//内部类使用枚举类
private enum SingletonEnum {
INSTANCE;
private Singleton singleton;
//在枚举类的构造器里初始化singleton
SingletonEnum() {
singleton = new Singleton ();
}
private Singleton getSingleton() {
return singleton;
}
}
//对外提供获取单例的方法
public static Singleton getInstance(){
return SingletonEnum.INSTANCE.getSingleton();
}
}
2.6.2 枚举代码实现要点解析
1、内部类使用枚举类
2、在枚举类的构造器里初始化singleton,提供私有方法供本类获取singleton
3、对外提供获取单例的方法
2.6.3 枚举实现优点
1、支持延迟加载,可做到按需加载
2、支持高并发
3、防止反射和反序列化攻击
总的来说推荐使用。