最近一直在刷数据结构(leetcode),嘿嘿,看看之前写的专栏一直没有动手,哎,不是我不想写,是因为积累确实没到
如同军人没经历过几场百团大战,你能让他去带军大战么,这不是纸上谈兵么
好了回归正题:
单例其实很简单,但其实里面还是有很多知识点可以学习,
- 比如static修饰的东西(方法或者变量),在程序中是什么时候加载的?
- 多线程中的volatile,synchronized?
- 内部类在java中的加载机制?
这三个问题搞定基本常用的单例就说完了
准备好就打开sublime开撸吧,别跟我说你是用ide准备写代码,ide中写的代码永远不是你的水平,面试的时候,你还是得手写呢
我是已经手写一遍了,嗯,写的还不错就是有几个单词忘记了(>﹏<。)
1.饿汉模式
其实这种模式对于我们开发者来说才是真正的懒汉模式,直接用一个static修饰变量,一个方法返回就行了
/**
* 饿汉模式
*/
public class Singleton{
private static Singleton sl=new Singleton();
public static Singleton getInstance(){
return sl;
}
public void sayhello(){
System.out.println("say hello");
}
}
我们来说第一个问题
比如static修饰的东西(方法或者变量),在程序中是什么时候加载的?
static修饰的东西,在程序一开始加载中,内存单独开启了一块区域给静态变量区域,他们是最先被加载的,而对象的引用都会指向同一个内存区域
总结:
优点:写起来简单,容易实现,多线程安全
缺点:没有考虑使用的时候才进行加载
2.懒汉模式
懒汉模式呢,就比较多版本,当然我也是一步一步写的
第一个版本,解决用时加载问题,当然是没有考虑到多线程使用
/**
* 懒汉式单例
*/
public class Singleton {
private static Singleton sl=null;
public static Singleton getInstance(){
if(null==sl){
sl=new Singleton();
}
return sl;
}
}
第二个版本呢,纠结了多线程的问题了,这里就用到了synchronized关键字了
/**
* 线程安全的耗时做法
*/
public class Singleton{
private static Singleton sl=null;
public static synchronized Singleton getInstance(){
if(null==sl){
sl=new Singleton();
}
return sl;
}
}
第三个版本呢,直接用synchronized同步整个方法很耗时呢,毕竟有可能他不为空呢,你也去同步是不是就多此一举呢
public class Singleton {
private static volatile Singleton sl = null;
public static Singleton getInstance() {
if (null == sl) {
synchronized (Singleton.class) {
if (null == sl) {
sl = new Singleton();
}
}
}
return sl;
}
}
细心的朋友,应该会想,是不是缺了点什么,因为单例大家都知道用,也会写
你们再看看,是不是多了一个volatile关键字
这里引出了二个问题
多线程中的volatile,synchronized使用
在单例中呢,synchronized使用了同步代码块,但是呢,如果不加volatile,就会出错为什么呢?
关键在于sl = new Singleton(); 在内存中是这样的
inst = allocat(); // 分配内存
sSingleton = inst; // 赋值
constructor(inst); // 真正执行构造函数
为什么会这样呢,因为jvm优化,指令重排序,赋值在前面,真正去执行构造函数可能在后面,而volatile就是屏蔽指令重排序,这样就保证了多线程安全
这里就不牵扯太多多线程了,毕竟写的是设计模式
3.静态内部类
public class Singleton {
private static class Holder {
private static Singleton sl = new Singleton();
}
public static Singleton getInstance() {
Holder holder = new Holder();
return holder.sl;
}
}
看到这个写法,你肯定很萌比,这能解决什么问题呢
按需加载:
无论是静态还是非静态内部类都是在第一次使用时才会被加载,外部类不调用getInstance,内部类是不会被加载的
多线程安全:
类加载的时候有一种机制叫做 缓存机制;第一次加载成功之后会被缓存起来,所以基本上实现了和饿汉模式一样的效果
End