单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
所谓类的单例模式,就是采取一定的方法保证在整个软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(一般是静态方法)。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
单例模式保证了该类在系统内存中只存在一个对象,节省了系统资源,对于一些要频繁创建与销毁的对象,使用单例模式可以提高系统性能。
许多时候,整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息,显然,这种方式简化了在复杂环境下的配置管理。
需要频繁的进行创建和销毁的对象、创建对象时耗时过多或者耗费资源过多(重量级对象)但又经常用到的对象、工具类对象、频繁访问数据库和文件的对象(比如数据源、session工厂等)
1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例。
2、避免对资源的多重占用。
饿汉式(静态常量)
饿汉式(静态代码块)
懒汉式(线程不安全)
懒汉式(线程安全,同步方法)
懒汉式(线程安全,同步代码块)
双重检查
静态内部类
枚举
关键点
构造器私有化(防止 外部通过new创建实例)
在类的内部创建对象
向外暴露一个静态的公共方法(getInstance)
代码实现
/**
* @Description: 单例模式-饿汉式(静态常量)
* @Author: atong
* @Date: 2020-06-09
* @Version:v1.0
*/
public class Singleton01 {
public static void main(String[] args) {
//测试
Singleton singleton = Singleton.getInstance();
Singleton singleton1 = Singleton.getInstance();
System.out.println(singleton == singleton1);
System.out.println(singleton.hashCode());
System.out.println(singleton1.hashCode());
}
}
class Singleton {
//私有构造函数,方式外部通过new创建对象
private Singleton () {
}
//类的内部创建对象
private final static Singleton singleton = new Singleton();
//对外暴露一个静态方法
public static Singleton getInstance() {
return singleton;
}
}
优点
写法简单,在类装载的时候完成实例化。避免线程同步问题。
缺点
在类加载的时候就进行实例化,没有达到Lazy Loading的效果,如果从始至终没有使用过这个实例,则会造成内存浪费。
总结
可用,但可能造成内存浪费。
关键点
构造器私有化(防止 外部通过new创建实例)
类内部声明变量
静态代码块中,创建单例对象
向外暴露一个静态的公共方法(getInstance)
代码实现
/**
* @Description: Singleton02单例模式-饿汉式(静态代码块)
* @Author: atong
* @Date: 2020-06-10
* @Version:v1.0
*/
public class Singleton02 {
public static void main(String[] args) {
//测试
Singleton singleton = Singleton.getInstance();
Singleton singleton1 = Singleton.getInstance();
System.out.println(singleton == singleton1);
System.out.println(singleton.hashCode());
System.out.println(singleton1.hashCode());
}
}
class Singleton {
//私有构造函数,方式外部通过new创建对象
private Singleton () {
}
//类的内部声明变量
private static Singleton singleton;
//在静态代码块中创建单例对象
static {
singleton = new Singleton();
}
//对外暴露一个静态方法
public static Singleton getInstance() {
return singleton;
}
}
优点
类的实例化过程放到了静态代码块中,也是在类加载的时候初始化实例。优缺点同上。
缺点
优缺点同上
总结
可用,但可能造成内存浪费。
关键点
构造器私有化(防止 外部通过new创建实例)
类内部声明变量
向外暴露一个静态的公共方法(getInstance),当调用该方法时,才去创建实例。
代码实现
/**
* @Description: Singleton03单例模式-懒汉式(线程不安全)
* @Author: atong
* @Date: 2020-06-13
* @Version:v1.0
*/
public class Singleton03 {
public static void main(String[] args) {
//测试
Singleton singleton = Singleton.getInstance();
Singleton singleton1 = Singleton.getInstance();
System.out.println(singleton == singleton1);
System.out.println(singleton.hashCode());
System.out.println(singleton1.hashCode());
}
}
class Singleton {
//私有构造函数,方式外部通过new创建对象
private Singleton () {
}
//类的内部声明变量
private static Singleton singleton;
//对外暴露一个静态方法,当调用该方法时,才去创建实例(singleton)
public static Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
优点
能起到Lazy Loading的效果,但只能在单线程下使用。
缺点
如果在多线程下,一个线程进入了if(singleton ==null)判断语句块,还没来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。
结论
在实际开发中,禁止使用这种方式。
关键点
构造器私有化(防止 外部通过new创建实例)
类内部声明变量
向外暴露一个静态的公共方法(getInstance),当调用该方法时,才去创建实例。且添加synchronized修饰,防止线程安全。
代码实现
/**
* @Description: Singleton04单例模式-懒汉式(线程安全)
* @Author: atong
* @Date: 2020-06-15
* @Version:v1.0
*/
public class Singleton04 {
public static void main(String[] args) {
//测试
Singleton singleton = Singleton.getInstance();
Singleton singleton1 = Singleton.getInstance();
System.out.println(singleton == singleton1);
System.out.println(singleton.hashCode());
System.out.println(singleton1.hashCode());
}
}
class Singleton {
//私有构造函数,方式外部通过new创建对象
private Singleton () {
}
//类的内部声明变量
private static Singleton singleton;
//对外暴露一个静态方法,当调用该方法时,才去创建实例(singleton)
//添加synchronized修饰符,解决线程安全问题
public static synchronized Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
优点
解决了线程不安全问题
缺点
效率低
总结
在实际开发中,不推荐使用。
关键点
构造器私有化(防止 外部通过new创建实例)
类内部声明变量,并使用volatile修饰(禁止指令重排)
先判空–>同步代码块–>再判空–>为null则创建对象(双重检查)
代码实现
/**
* @Description: Singleton05单例模式-双重检查(Double-Check)
* @Author: atong
* @Date: 2020-06-15
* @Version:v1.0
*/
public class Singleton05 {
public static void main(String[] args) {
//测试
Singleton singleton = Singleton.getInstance();
Singleton singleton1 = Singleton.getInstance();
System.out.println(singleton == singleton1);
System.out.println(singleton.hashCode());
System.out.println(singleton1.hashCode());
}
}
class Singleton {
//私有构造函数,方式外部通过new创建对象
private Singleton () {
}
//类的内部声明变量
//volatile防止指令重排
private static volatile Singleton singleton;
//对外暴露一个静态方法,当调用该方法时,才去创建实例(singleton)
//加入双重检查,解决线程安全问题,同时支持Lazy Loading,同时保证了效率
//推荐使用
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
优点
线程安全;Lazy Loading;效率高
总结
推荐使用
关键点
外部类进行类装载的时候,静态内部类不会跟着进行类装载
当调用getInstance方法时,会使静态内部类加载,且只会装载一次,在装载的时候线程是安全的,既可以保证线程安全也可以起到Lazy Loading作用。
代码实现
/**
* @Description: Singleton06单例模式-静态内部类
* @Author: atong
* @Date: 2020-06-15
* @Version:v1.0
*/
public class Singleton06 {
public static void main(String[] args) {
//测试
Singleton singleton = Singleton.getInstance();
Singleton singleton1 = Singleton.getInstance();
System.out.println(singleton == singleton1);
System.out.println(singleton.hashCode());
System.out.println(singleton1.hashCode());
}
}
class Singleton {
//私有构造函数,方式外部通过new创建对象
private Singleton () {
}
//静态内部类,该内部类含有一个静态属性Singleton
private static class SingletonInstance {
private static final Singleton INSTANCE = new Singleton();
}
//对外暴露一个静态方法,直接返回SingletonInstance.INSTANCE
public static Singleton getInstance() {
return SingletonInstance.INSTANCE;
}
}
优点
采用类装载机制保证初始化实例时只有一个线程
类的静态属性只会第一次加载类的时候初始化,所以这里,JVM帮助我们保证了线程安全性,在类的初始化时,别的线程是无法进入的。
总结
线程安全;Lazy Loading;效率高;推荐使用
代码实现
/**
* @Description: Singleton07单例模式-枚举
* @Author: atong
* @Date: 2020-06-15
* @Version:v1.0
*/
public class Singleton07 {
public static void main(String[] args) {
Singleton singleton = Singleton.INSTANCE;
Singleton singleton1 = Singleton.INSTANCE;
System.out.println(singleton == singleton1);
System.out.println(singleton.hashCode());
System.out.println(singleton1.hashCode());
singleton.sayOk();
}
}
enum Singleton {
INSTANCE;
public void sayOk() {
System.out.println("ook");
}
}
优点
借助JDK1.5中添加的枚举来实现单例模式。不仅能避免多线程同步问题,还能防止防止反序列化重新创建新的对象。