对于单例,最早可追溯到我上学那会儿的课程实习,那时候有企业里的员工给我们进行软件编程培训,那会儿第一次听说单例的概念。
在印象里,
问:“你们知道如何只创建出一个对象吗?”
答:“不知道。为什么只要创建一个对象”。
问:“听说过单例吗?”
答:“没有,单例是干啥的?”
解释:“单例就是保证程序运行期间,保证某一个类永远只有一个对象,这样既能节省空间,又能节省创建时间的开销”。
我们:“他这是说的什么意思?????他在说什么??”。
其实,当我们接触到Spring之后,对于它的Bean的创建,其实就是单例,就这样糊里糊涂的开始接触单例了。
单例的实现。
1.懒汉式单例。
/**
* 单例测试
*/
public class Singleton {
private static Singleton singleton = null;
//私有构造函数
private Singleton(){
}
private static Singleton getSingleton(){
if(null == singleton){ //1
singleton = new Singleton(); //2
}
return singleton; //3
}
}
这是平时写的最多的了,懒汉式,就是很懒,当用到的时候才去执行new的操作,只要不使用,就不会占用空间。这可能能算是优点吧。
缺点:当然,如果你从事开发时间不长,不会去在意多线程的情况,如果考虑了多线程,我们先来看看多线程下会出现什么问题。
线程A和线程B同时进来,步骤1时同时发现singleton为null,都去创建singleton对象,shit了不是。
2.懒汉式单例---线程同步 。
基于懒汉式单例,如果出现上述情况咋办,加锁呗,synchronized解决问题。不过加上之后总觉得变扭。大多数时候又用不上。
/**
* 单例测试
*/
public class Singleton {
private static Singleton singleton = null;
//私有构造函数
private Singleton(){
}
public static synchronized Singleton getSingleton(){
if(null == singleton){
singleton = new Singleton();
}
return singleton;
}
}
3.饿汉式单例
/**
* 单例测试
*/
public class Singleton {
private static Singleton singleton = new Singleton();
//私有构造函数
private Singleton(){
}
public static Singleton getSingleton(){
return singleton;
}
}
饿汉式效果也很明显,首先singleton只在类加载的时候就进行了初始化,也就new对象,同时也避免了多线程的问题。
缺点:非要说缺点的话,就是不管你用不用,它就在那儿了,如果这个单例对象占用空间很大的话,同时只在特定场景才出现,就有点得不偿失了这样。
3.静态内部类
饿汉式的缺点也很明显,那就是即使不使用,也会存在,那么能不能在它使用时才让它new呢?可以啊,静态内部类不就可以解决嘛。
/**
* 单例测试
*/
public class Singleton {
private static class SingletonHolder{
private static Singleton singleton = new Singleton();
}
//私有构造函数
private Singleton(){
System.out.println("ss");
}
public static Singleton getSingleton(){
return SingletonHolder.singleton;
}
}
既达到了懒汉式的效果,又线程安全。
4.枚举方式
/**
* 单例测试
*/
public class Singleton {
public static void main(String[] args) {
SingletonEnum.SINGLETON.method();
}
}
enum SingletonEnum{
SINGLETON;
public void method(){
System.out.println("see this is enum singleton");
}
}
第一次看见这种方式时真的,真的被惊呆了,还能这么玩。。。既是一个实例,又能保证线程安全。。。另外还有一个就是序列化问题,如果单例实现了序列化接口,那么就不能保证是唯一实例了,但是枚举保证了能被自由序列化,这个原因我这边不想过多探讨,知道一下,后续研究。这也是最推荐的方式。
4.双重检查机制
双重检查机制最精髓的地方在于volatile的运用,至于这个关键字的作用,我不想再去说了。可以参考下面的链接。
/**
* 单例测试
*/
public class Singleton {
private static volatile Singleton singleton = null;
private Singleton(){
}
private static Singleton getSingleton(){
if(null == singleton){
synchronized (Singleton.class){
if(null == singleton){
singleton = new Singleton();
}
}
}
return singleton;
}
}
一般饿汉式比较普遍,枚举的方式太冷僻了,但是我觉得确实最好的,看过之后决定以后都这么写。
双重检查感觉太啰嗦,但是其他没什么,面试的时候双重检查也是可以深究问下去很多东西。比如static,比如volatile,比如类加载,比如synchronized。