小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
本文已参与「掘力星计划」,赢取创作大礼包,挑战创作激励金。
概述
确保一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。可以有效减少内存的开支,针对需要频繁创建、销毁的对象优势非常显著。
分类
单例模式有很多实现方法, 懒汉, 饿汉, 静态内部类, 枚举类,但整体分类有以下两种:
懒汉式单例
类加载不会导致该单实例对象被创建, 而是首次使用该对象时才会创建。
饿汉式单例
类加载就会导致该实例对象被创建。
起因
小张:哥,你现在有没有空,能不能来帮我看一下这接口?
我:什么情况呢?
小张:测试那边说我这个接口响应时间太长了,量一起来很多请求都超时了,让我得重新优化一下,我看了那接口就是一个查询功能,查询的SQL索引也都命中了,耗时也在合理范围内,不知道要怎么去优化他了。
我:那你把代码给我看看。
public class Client {
public static void main(String[] args) {
long start = System.currentTimeMillis();
// 创建数据库连接
connection();
// 执行查询
select();
// 释放连接
destroy();
System.out.println((System.currentTimeMillis() - start) / 1000 + "秒");
}
public static void connection(){
try {
// 模拟连接耗时
Thread.sleep(2500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void select(){
try {
// 模拟操作耗时
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void destroy(){
try {
// 模拟销毁耗时
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
我:你这个接口查询语句我看了没什么问题,耗时太长的问题主要存在于你创建的数据库连接跟销毁的步骤上了,对于一个需要经常使用的对象来说,我们并不需要频繁的创建和销毁它,这里你可以使用单例模式来对它进行一个改造。
案例目标
这里的类加上final关键字修饰,主要是怕有子类对父类覆盖,破坏了单例。
同时,如果该类实现了序列化的话,我们还得实现一个readResovle()方法,使之在反序列化中返回你当前的对象,如果没有实现readResovle()方法的话,在反序列化时会创建一个新的实例。
饿汉模式
public final class HungrySingleton implements Serializable {
private static HungrySingleton jdbc = new HungrySingleton();
// 构造方法私有,保证外界无法直接实例化
private HungrySingleton(){}
public Object readResovle(){ return jdbc; }
// 通过该方法获得实例对象
public static HungrySingleton getInstance(){
return jdbc;
}
}
懒汉模式
public final class LazySingleton {
// volatile 防止指令重排
private static volatile LazySingleton jdbc = null;
// 构造方法私有,保证外界无法直接实例化
private LazySingleton(){}
// 同步代码块缩小加锁范围,提升性能
public static LazySingleton getInstance(){
if (jdbc != null){
return jdbc;
}
synchronized(LazySingleton.class){
// 再次进行判空,防止并发问题
if(jdbc != null){
return jdbc;
}
jdbc = new LazySingleton();
return jdbc;
}
}
小结
final:可以防止当前类被子类覆盖。
readResovle():方法可以在反序列化时返回单前的实例对象。
volatile:可以防止指令重排。
饿汉:类加载就会导致该实例对象被创建。
懒汉:类加载不会导致该单实例对象被创建, 而是首次使用该对象时才会创建。