Java 中 再一次看单例模式

来源:互联网 发布:淘宝可以用什么支付 编辑:程序博客网 时间:2024/04/30 19:59

还是那家伙,上一次看了我的博客说我 没理解单例模式,当时很郁闷,虽然我当时知道我没理解单例模式,但是很郁闷 很纠结,为啥还是不懂呢,现在总结一下找找问题,再一次反击他 生气.

我感觉单例模式 就是类只能有一个对象,我能想到的就是把这个类的构造函数定义成private 应该就可以了,然后外界不能访问他,怎么办? 可以给他们提供一个接口,返回这个类的方法,这样他们就可以用这个方法了,但是不能构造对象了.

<span style="font-size:14px;">class Test{private static final Test t = new Test();private Test(){}public static Test getTest(){return t;}}</span>

哈哈哈大笑,无论别人调用多少次getTest其实这个类就一个对象,这样就实现了单例模式得意.

但是再一想不对啊,不管用不用这个对象肯定会被创建的,如果不用也创建就不是很好了,费时费内存难过

<span style="font-size:14px;">class Test{private static Test t = null;private Test(){}public static Test getTest(){if (t == null)t = new Test();return t;}}</span>

这样就好了,null 就重新创建,不为null就不用创建了,多省事得意.

但是还是不行又有了线程同步问题,万一线程A走到if里面还没有new,但是线程B运行了并且判断了t为null,这个时候B也进if里面了,这个时候就有了两个对象了不是单例模式了委屈,但是仔细一想 不就是线程同步问题吗,好解决,方法前面用synchronized修饰就行了,加上同步锁,OK解决了生气.

但是上面的胡能胡能菜鸟还行,大牛一看就大发雷霆了,这TMD的效率不慢死啊,仔细一想也是, StringBuilder 和 StringBuffer 就知道了,synchronized这哥们效率低.

为了兼容效率和正确性,只能代码块同步了,这样好点,代码如下:

<span style="font-size:14px;">class Test{private static Test t = null;private Test(){}public static Test getTest(){if (t == null){synchronized(Test.class){if (t == null)t = new Test();}}return t;}}</span>

这样就OK了.

但是但是

下面我们开始说编译原理。所谓编译,就是把源代码“翻译”成目标代码——大多数是指机器代码——的过程。针对Java,它的目标代码不是本地机器代码,而是虚拟机代码。编译原理里面有一个很重要的内容是编译器优化。所谓编译器优化是指,在不改变原来语义的情况下,通过调整语句顺序,来让程序运行的更快。这个过程成为reorder。 

要知道,JVM只是一个标准,并不是实现。JVM中并没有规定有关编译器优化的内容,也就是说,JVM实现可以自由的进行编译器优化。 

下面来想一下,创建一个变量需要哪些步骤呢?一个是申请一块内存,调用构造方法进行初始化操作,另一个是分配一个指针指向这块内存。这两个操作谁在前谁在后呢?JVM规范并没有规定。那么就存在这么一种情况,JVM是先开辟出一块内存,然后把指针指向这块内存,最后调用构造方法进行初始化。 

下面我们来考虑这么一种情况:线程A开始创建SingletonClass的实例,此时线程B调用了getInstance()方法,首先判断instance是否为null。按照我们上面所说的内存模型,A已经把instance指向了那块内存,只是还没有调用构造方法,因此B检测到instance不为null,于是直接把instance返回了——问题出现了,尽管instance不为null,但它并没有构造完成,就像一套房子已经给了你钥匙,但你并不能住进去,因为里面还没有收拾。此时,如果B在A将instance构造完成之前就是用了这个实例,程序就会出现错误了! 

以上这段不是太懂,再说吧。。。应该是这样的.......

但是解决方案就是这样的

<span style="font-size:14px;">class Test{private volatile static Test t = null;private Test(){}public static Test getTest(){if (t == null){synchronized(Test.class){if (t == null)t = new Test();}}return t;}}</span>

就是使用了volatile修饰符JDK1.5才用的.

0 0