单例模式
noob网站的介绍:
地址:http://www.runoob.com/design-pattern/singleton-pattern.html 0.0
探探原理
饿汉模式:
//饿汉式
public class Demo1 {
//1.私有的构造方法
//2.返回实例的值
//3.类中自己创建实例
private static Demo1 instance = new Demo1();
private Demo1() {
}
public static Demo1 getInstance() {
return instance;
}
public void doSomeThing() {
System.out.println("单例单例");
}
}
说明:
- 为什么代码要这么写啊?
- 构造器私有:单例单例,只能有一个实例,别人不能new你这个类,所以构造器写成private私有的,这样只能你自己使用.
- 这个类的作用是什么?为别人提供一个对象使用,所以getInstance负责给别人一个对象
- 这个对象来自何处?构造器只能自己用,只能自己创建了咯~
- 这个对象提供给别人,干什么呢?doSomeThing实例方法给这个实例调用
- 为什么叫饿汉啊?不管三七二十一,只要类被初始化,我就new一个对象进内存里面.PS,你知道类加载的时候,静态域(静态变量)会被初始化不?
- 线程安全不?安全,因为类加载只有一个线程干啊
- 缺点:如果这个对象比较大,比价复杂,不管三七二十一就往内存里面扔一个对象,那势必造成占用吧?
首先你要理解这段代码,因为这是单例模式最常用的一种.让我们接着往下看
懒汉模式:
// 懒汉式-非线程安全
class Demo2 {
private static Demo2 instace;
private Demo2() {
}
private static Demo2 getInstace() {
if (instace == null) {
instace = new Demo2();
}
return instace;
}
public void doSomeThing() {
System.out.println("单例单例");
}
}
注意:
- 为什么懒? 它在类加载的时候不会立刻new一个对象进内存,而是先看看这个对象是不是已经被new出来了
- 线程安全吗?不安全,当两个线程同时getInstace时候,都判断instace是空,都new 了一个,这就不是单例模式了把?变成双例模式了,就是这个道理
懒汉-安全:
// 懒汉式-线程安全
class Demo3 {
private static Demo3 instance;
private Demo3() {
}
public synchronized static Demo3 getInstance() {
if (instance == null) {
instance = new Demo3();
}
return instance;
}
public void doSomeThing() {
System.out.println("单例单例");
}
}
添加了类级同步锁,访问变成同步的,但是效率比较低,多个线程调用getInstance的时候一定会阻塞.这个时候DCL登场
DCL:双重检测 double-checked locking:
// DCL -双重检测
class Demo4 {
private volatile static Demo4 instance;
private Demo4() {
}
private static Demo4 getInstance() {
if (instance == null) {
synchronized (Demo4.class) {
if (instance == null) {
instance = new Demo4();
}
}
}
return instance;
}
public void doSomeThing() {
System.out.println("单例单例");
}
}
DCL设计出来就是为了在懒汉模式下,继续提高性能
- volatile关键字,这个关键字保证3个特性,可见性,有序性,原子性.这里主要是防止指令重排序,(新入门的同学可能不懂这个,建议搜搜,值得一学,是JVM原理)http://www.infoq.com/cn/articles/double-checked-locking-with-delay-initialization,这个链接讲的很细致了,可以进去看看.
- 两次检查提高了性能,把第一次检查,放在同步代码块外面,只要有了第一个new,之后的就不需要进入同步代码块了
PS:DCL双重检查,说实话,挺复杂的,真正用这个,感觉不是太好,noob最后的经验之谈也说明了这个问题.
静态代码块:
// 登记,静态内部类
class Demo5 {
private static class Holder
{
private static final Demo5 INSTACE = new Demo5();
}
private Demo5() {
}
private static Demo5 getInstace() {
return Holder.INSTACE;
}
public void doSomeThing() {
System.out.println("单例单例");
}
}
- 线程安全,使用的是类加载的原理
- 属于懒汉模式,因为只用调用getInstace()的时候,才会加载静态内部类,进行静态变量的初始化
枚举:
enum Demo6 {
SINGLE;
public void doSomeThing() {
System.out.println("单例单例");
}
}
小小枚举,代码很少,含知识量高~参见下面的这个链接,讲的很好,挖的都是底层,也是利用类加载的机制,实现Demo6的单例化,
http://www.hollischuang.com/archives/197
总结
小小单例,6种方法,饿汉常用,懒汉12性能太低,不可能被用,DCL太复杂,若是要延迟找静态,枚举类完爆一切zzz写法简单,功能强大