官方定义
所谓类的单例设计模式,就是采取一定的方法保证在整个软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法)
举个最常见的例子,Sping中的bean默认都是单例模式,每个bean定义只生成一个对象实例,每次getBean请求获得的都是此实例。
单例模式的八中写法
- 饿汉式(静态常量)
- 饿汉式(静态代码块)
- 懒汉式(线程不安全)
- 懒汉式(线程安全,同步方法)
- 懒汉式(线程安全,同步代码块)
- 双重检查
- 静态内部类
- 枚举方法
验证法:
package singletonpattern.hungrydemo;
public class SingletonDemo {
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();
System.out.println(instance == instance1);//true
System.out.println(instance.hashCode());
System.out.println(instance1.hashCode());
}
}
饿汉式(静态常量)
class Singleton {
//一、构造器私有化,防止外部用构造器
private Singleton() {
}
//二、类的内部创建对象 final static
private static final Singleton singleton = new Singleton();
//三、对外提供公共的静态方法,返回该类唯一的对象实例
public static Singleton getInstance() {
return singleton;
}
}
写法分析
- 优势:简单 避免多线程的同步问题
- 劣势:没有实现懒加载的效果,内存的浪费
饿汉式(静态代码块)
class Singleton {
//一、构造器私有化,防止外部用构造器
private Singleton() {
}
//二、类的内部创建对象 final static
private static final Singleton singleton;
static {
singleton = new Singleton();
}
//三、对外提供公共的静态方法,返回该类唯一的对象实例
public static Singleton getInstance() {
return singleton;
}
}
写法分析
- 优势:简单 避免多线程的同步问题
- 劣势:没有实现懒加载的效果,内存的浪费
懒汉式(线程不安全)
class Singleton {
// 构造器私有化
private Singleton() {
}
//类的内部提供对象
private static Singleton singleton;
//对外提供公共方法的静态方法的时候,来判断
public static Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
写法分析
- 优势:起到了懒加载的效果,不会造成内存浪费
- 劣势:线程不安全
懒汉式(线程安全,同步方法)
class Singleton {
// 构造器私有化
private Singleton() {
}
//类的内部提供对象
private static Singleton singleton;
//对外提供公共方法的静态方法的时候,来判断
public static synchronized Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
懒汉式(线程安全,同步代码块)
class Singleton {
// 构造器私有化
private Singleton() {
}
//类的内部提供对象
private static Singleton singleton;
//对外提供公共方法的静态方法的时候,来判断
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class){
singleton = new Singleton();
}
}
return singleton;
}
}
不推荐的,解决不了的线程安全问题
双重检查
class Singleton {
private Singleton() {
}
private static Singleton singleton;
//加入双重检查机制
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
写法分析
实际开发中推荐使用这种方法
- 线程安全 解决了线程安全问题
- 懒加载 不会造成内存的浪费
- 效率很高
可能出现的问题
我们认为的new Singleton()操作
1)分配内存地址M
2)在内存M上初始化Singleton对象
3)将M的地址赋值给instance变量
JVM编译优化后(指令重排)可能new Singleton()操作
1)分配内存空间M
2)将M的地址赋值给instance变量
3)在内存M上初始化Singleton对象
会有可能发生空指针异常解决的方式
只需要一个关键字 volatile 来禁止指令重排
class Singleton {
private Singleton() {
}
private static volatile Singleton singleton;
//加入双重检查机制
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
扩展-Volatile
轻量级的同步机制(低配版)没有保证原子性三大特性?保证可见性
其中一个线程修改了主内存变量的值,要写回主内存,并要及时通知其他内存可见没有保证原子性
(没法)不能保证不可分割,完整,要么同时成功,要么同时失败禁止指令重排
和底层内存屏障相关,避免多线程下出现指令乱序的情况
静态内部类
class Singleton {
private Singleton() {
}
private static class SingletonInstance {
public static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonInstance.INSTANCE;
}
}
写法分析
- 不会出现线程安全问题
- JVM来帮我们保证线程的安全性
- 利用静态内部类的特点
枚举方法
enum Singleton{
INSTANCE;//属性
}
写法分析
不仅可以避免线程安全问题 推荐大家使用
注意事项
1.单例模式保证了系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能。
2.当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是new
单例模式的使用场景
- 对于一些需要频繁创建销毁的对象
- 重量级的对象
- 经常使用
- 工具类对象
- 数据源
- ...