『单例模式』是一种建立型的设计模式,保证一个类只有一个实例,并提供一个访问它的全局访问点。java
在一个系统中,一个类常常会被使用在不一样的地方,经过单例模式,咱们能够避免屡次建立多个实例,从而节约系统资源。git
单例模式每每有三个特征,一个类只能有一个实例,它必须自行提供实例的建立,它必须提供方法暴露此实例。github
饿汉老是一次吃个饱,因此这种方式老是在系统初始化的时候建立全部的对象,无论会不会什么时候被使用。面试
public class SingleTon {
//自行构造实例
private static final SingleTon instance = new SingleTon();
//置空构造器,不容许外部构造
public SingleTon(){}
//对外暴露内部实例
public SingleTon getInstance(){
return this.instance;
}
}
复制代码
饿汉方式实现的单例模式是极其简单的,但缺点也很明显,即使这个类一时半会不会被使用到,但也必须在编译的时候初始化分配堆内存,建立这个内部实例。设计模式
懒汉很懒,只有在系统用到某个类的实例的时候,才会实例化出一个惟一实例。安全
public class SingleTon {
private static SingleTon instance= null;
private SingleTon(){}
public static SingleTon getInstance(){
if(null == instance){
instance = new SingleTon();
}
return instance;
}
}
复制代码
instance 在类编译的时候没有初始化,而只有在调用 getInstance 方法的时候,才会去实例化 instance。微信
looks pretty!markdown
多线程环境下,线程 A 和线程 B 同时判断 instance==null,都去实例化 instance,致使 instance 被实例化两次,堆中产生一个无引用对象,并发量大的状况下,会有更多的无用对象被建立,甚至可能提早触发 GC。多线程
线程不安全,相信你第一时间也会想到加锁控制,那你是否是也这么加的呢?并发
public class SingleTonLock {
private static SingleTonLock instance= null;
public SingleTonLock(){}
public synchronized SingleTonLock getInstance(){
if (instance == null){
instance = new SingleTonLock();
}
return instance;
}
}
复制代码
这种方式直接给 getInstance 方法加锁了,很明显,会形成大量无效的锁等待,继续优化。
public class SingleTonLock {
private static volatile SingleTonLock instance= null;
public SingleTonLock(){}
public SingleTonLock getInstance(){
if (instance == null){
synchronized(this){
//再次判断是为了防止有的线程醒来之后再次实例化
//有可能其余线程已经实例化完成了
if (instance == null){
instance = new SingleTonLock();
}
}
}
return instance;
}
}
复制代码
给 instance 加 volatile 修饰是为了防止 jvm 指令重排序,经过再次判断能够保证此实例的惟一实例化。
这的确是一种不错的懒汉实例,推荐你们使用,但我更推荐下一种。
我的认为使用枚举类实现懒汉单例模式是最佳实践,枚举类本质上是用静态字段来实现的,例如:
public enum Color {
RED(), GREEN(), BLUE(), YELLOW();
}
复制代码
javap 反编译这个枚举类获得:
public final class com.example.test.lazy.Color extends java.lang.Enum<com.example.test.lazy.Color> {
public static final com.example.test.lazy.Color RED;
public static final com.example.test.lazy.Color GREEN;
public static final com.example.test.lazy.Color BLUE;
public static final com.example.test.lazy.Color YELLOW;
public static com.example.test.lazy.Color[] values();
public static com.example.test.lazy.Color valueOf(java.lang.String);
static {};
}
复制代码
那么,枚举如何实现单例模式,上代码:
public class SingleTonE {
public static SingleTonE getInstance(){
return SingleTonEnum.SINGLETON.getInstance();
}
private enum SingleTonEnum{
SINGLETON;
private SingleTonE instance;
SingleTonEnum(){
instance = new SingleTonE();
}
public SingleTonE getInstance(){
return this.instance;
}
}
}
复制代码
只有当调用 getInstance 方法获取实例的时候,才会触发枚举类的加载,而后按照上面说的,生成一个静态字段并初始化其内部的单例 instance,由于 jvm 保证只能一个线程进行类加载,因此整个过程看起来很是的简单。
我的认为,枚举类实现单例模式是一种最佳实践,推荐你应用到本身的项目。
近期会整理一个设计模式系列,分别讲讲 23 种设计模式,感兴趣的能够关注下哦~
关注公众不迷路:有诗有酒有代码。
公众号回复「1024」加做者微信一块儿探讨学习!
公众号回复「面试题」送你一份面试题以及做者的做答答案
每篇文章用到的全部案例代码素材都会上传我我的 github
github.com/SingleYam/o… 欢迎来踩!