单例模式
单例模式是什么
单例模式,是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中,应用该模式的一个类只有一个实例。
为什么要有单例模式
从开发者本身来考虑的。比如配置文件,如果是一样的配置文件且不是单例的,就浪费了很多资源,而且也不知道是依哪一个为准。
当我们在应用中遇到功能性冲突的时候就需要使用单例模式。
//懒汉式单例类, 线程不安全 在第一次调用的时候实例化自己
public class Singleton01 {
// 三部曲
//1.私有构造方法,不让外界进行创建对象。
private Singleton01() {
}
//2.声明一个静态变量保存单例的引用
private static Singleton01 singleton = null;
//3.通过提供一个静态方法来获取我们的单例
//在多线程环境使用在该单例实现是不安全的,不推荐使用此写法
public static Singleton01 getInstance() {
if (singleton == null) {
singleton = new Singleton01();
}
return singleton;
}
}
线程是其不安全的,在实际开发应用中不推荐此写法。
//懒汉式单例 保证线程安全
public class Singleton001 {
// 三部曲
//1.私有构造方法,不让外界进行创建对象。
private Singleton001() {
}
//2.声明一个静态变量保存单例的引用
private static volatile Singleton001 singleton = null;
//3.通过提供一个静态方法来获取我们的单例
//在多线程环境使用在该单例实现是线程安全的
//添加了synchronized的悲观锁修饰,即一次只有一个线程能访问该方法。抑制住了我们的性能消耗,造成资源的浪费
public static synchronized Singleton001 getInstance() {
if (singleton == null) {
singleton = new Singleton001();
}
return singleton;
}
}
在其基础上添加了synchronized悲观锁修饰,即一个时间点只能有一个线程访问该资源,没有最大限度的利用我们的资源,也不推荐。
//懒汉式单例 双重锁检查
public class Singleton0001 {
// 三部曲
//1.私有构造方法,不让外界进行创建对象。
private Singleton0001() {
}
//2.声明一个静态变量保存单例的引用
private static volatile Singleton0001 singleton = null;
//3.通过提供一个静态方法来获取我们的单例
//保证多线程环境下的另一种实现方式,双重锁检查
public static Singleton0001 getInstance() {
if (singleton == null) {
synchronized (Singleton0001.class) {
if (singleton == null) {
singleton = new Singleton0001();
}
}
}
return singleton;
}
}
该实现方式较为常用,引入了双重锁检查的机制,保证了线程的安全。
关于volatile关键字的使用,在单线程的环境下是不存在指令重排的情况,涉及到多线程就会涉及到指令重排。比如创建对象大致为三个过程:
1.开辟内存空间
2.创建对象。
3.对象指定内存空间。
在创建对象的过程中,多线程环境中是不一定会按照指令的顺序来进行创建对象。
所以需要添加volitile关键字,保证对象是创建完毕再继续使用。
//懒汉式单例 静态内部类
public class Singleton00001 {
// 三部曲
//1.私有构造方法,不让外界进行创建对象。
private Singleton00001() {
}
//2.静态内部类
private static class LazyHolder {
//防止内部误操作,不小心对其使用了代理或者其他的情况,final修饰,不允许改变
private static final Singleton00001 INSTANCE = new Singleton00001();
}
//3.通过提供一个静态方法来获取我们的单例
public static Singleton00001 getInstance() {
return LazyHolder.INSTANCE;
}
}
通过内部类来实现懒加载单例模式,该种实现方式也较为常用。
关于内部类实现懒加载的解释:类信息只有在使用其Class相关的时候才会加载字节码相关的类信息。静态内部类同样如此,只有在用到静态内部类的时候才会去用到我们内部类的静态相关信息。所以称其为懒加载。
//饿汉式(恶汉式)
public class Singleton02 {
// 三部曲
//1.私有构造方法,不让外界进行创建对象。
private Singleton02() {
}
//2.静态变量实例
private static Singleton02 singleton = new Singleton02();
//3.通过提供一个静态方法来获取我们的单例
//该单例模式是饿汉式,及加载该类字节码的时候该单例就已经被创建。
public static Singleton02 getInstance() {
return singleton;
}
}
该方式实现简单,也较为常用。
饿汉式:即在系统classloader的时候对象就应该被创建,不需要等到使用的时候再判断存在与否而创建。懒汉式反之,不赘述。
//类似spring里面的方法,按类名注册,下次从里面直接获取。
public class Singleton002 {
private static Map<String, Singleton002> maps = new HashMap<>();
static {
Singleton002 singleton = new Singleton002();
maps.put(singleton.getClass().getName(), singleton);
}
private Singleton002(){}
//静态工厂方法,返还此类唯一的实例
public static Singleton002 getInstance(String name) {
if (name == null) {
name = Singleton002.class.getName();
}
if (maps.get(name) == null) {
try {
//多态
maps.put(name, (Singleton002) Class.forName(name).newInstance());
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
return maps.get(name);
}
}
模仿着spring内部实现的单例模式,焕然一新的实现,值得推敲。建议使用。