【设计模式】单例模式

定义:
单例模式是一种常用的软件设计模式,其定义是单例对象的类只能允许一个实例存在。

典型案例:
jdk 源码 Rutime类 —— 饿汉式单例模式

一、饿汉式 (线程安全)

1.1 静态变量

实现步骤:
1)构造器私有化;(防止通过 new 得到实例对象)
2)类内部创建对象;
3)对外提供获取改实例对象的公有方法。

public class Hunger{
	//私有静态实例对象
	private static final Hunger instance=new Hunger();
	//私有构造器
	private Hunger(){
	}
	//对外提供获取实例对象的方法
	public static Hunger getInstance(){
		return instance;
	}
}

1.2 静态代码块

在静态代码块中实例化对象,其本质上和上面那种没什么分别。

public class Hunger{
	//私有静态实例对象
	private static Hunger instance;
	//静态代码块
	static{
		instance=new Hunger();
	}
	//私有构造器
	private Hunger(){
	}
	//对外提供获取实例对象的方法
	public static Hunger getInstance(){
		return instance;
	}
}

优点:
在类加载时就完成了对象实例化,避免了线程同步问题(线程安全)。

缺点:
如果始终没用使用这个实例对象,则会造成内存空间的浪费。

二、懒汉式

2.1 线程不安全的懒汉式

实现步骤:
1)私有构造器;
2)内部声明一个对象,但是不实例化;
3)提供一个公有方法,实例化并返回该对象。

public class Lazyer{
	//私有静态实例对象
	private static Lazyer instance;
	//私有构造器
	private Lazyer(){
	}
	//对外提供获取实例对象的方法
	public static Lazyer getInstance(){
		if(instance==null){
			instance=new Lazyer();
		}
		return instance;
	}
}

优点:
使用时才实例化对象,避免造成内存浪费;

缺点:
线程不安全:因为如果多个线程同时进入if(instance == null),可能会同时满足条件,从而创建出多个实例对象出来。

总结:
实际开发中不使用这种方式,因为存在潜在风险。

2.2 线程安全的懒汉式

在上一种懒汉式条件下使用同步方法。

public class Lazyer{
	//私有静态实例对象
	private static Lazyer instance;
	//私有构造器
	private Lazyer(){
	}
	//对外提供获取实例对象的同步方法
	public static synchronized Lazyer getInstance(){
		if(instance==null){
			instance=new Lazyer();
		}
		return instance;
	}
}

优点:
解决了线程不安全的问题。

缺点:
效率太低,每个线程想要获得该实例对象时都要进行线程同步。

总结:
实际开发中尽量不用这种写法。

2.3 同步代码块懒汉式 (线程不安全)

public class Lazyer{
	//私有静态实例对象
	private static Lazyer instance;
	//私有构造器
	private Lazyer(){
	}
	//对外提供获取实例对象的同步方法
	public static synchronized Lazyer getInstance(){
		if(instance==null){
			synchronized(Lazyer.class){
				instance=new Lazyer();
			}
		}
		return instance;
	}
}

缺点:
本质上和 2.1 没什么区别,实际开发中不能使用这种写法(但面试可能会考!)。

三、双重检查

使用 volatile 修饰内部对象,带同步代码块内外都检测一次对象是否为空。

public class Lazyer{
	//声明私有静态对象,用volatile修饰
	private static volatile Lazyer instance;
	//私有构造器
	private Lazyer(){
	}
	//对外提供获取实例对象的方法
	public static Lazyer getInstance(){
		if(instance==null){
			//同步代码块
			synchronized(Lazyer.class){
				if(instance==null){
					instance=new Lazyer();
				}
			}
		}
		return instance;
	}
}

优点:
线程安全、效率高、延迟加载。推荐使用这种单例设计模式。

四、静态内部类方式

内部类和静态内部类都是延时加载的,也就是说只有在明确用到内部类时才加载。
而且只会被加载一次,此时线程是安全的。

class Singleton{
	//私有构造器
	private Singleton(){}
	//静态内部类
	private static class  SingletonInstance(){
		private static final Singleton INSTANCE=new Singleton();
	}
	//获取实例的公有方法
	public static Singleton getInstence(){
		return SingletonInstance.INSTANCE;
	}
	
}

优点:
避免了线程不安全;利用静态内部类实现延迟加载,节约内存;效率高。推荐在实际开发中使用。

五、枚举方式

创建一个枚举类,仅具有一个实例对象。

enum Singleton_e{
	INSTANCE;
}

优点:
① 避免了多线程同步的问题;
② 防止通过反序列化和反射来创建对象。
推荐在实际开发中使用。

六、总结

1. 推荐使用的单例设计模式

① 静态内部类方式;
② 双重检查方式;
③ 饿汉模式;(如果确定对象会被使用)
④ 枚举方式。

2. 注意事项

① 单例模式保证了系统中某个类只存在一个实例对象,节省了系统资源,对于一些需要频繁创建和销毁的对象(重量级对象,频繁访问数据库或文件的对象),可以使用单例模式来设计;
② 实例化一个单例对象时,要使用对应的创建方法,而不是 new

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Nonoas

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值