对于编程人员来说,单例模式肯定不会陌生,无论你是否用过,至少你知道这是怎么一回事,就是一个类只有唯一的一个实例。对于代码,我们可以轻松加愉快的很快写出。
package com.baidu.test;
public class One {
private static One one = null;
private One() {}
public static One getInstance() {
if (null == one) {
one = new One();
}
return one;
}
}
这种方式去实现单例模式,是我们很容易想到的,一般我们称这种方式“懒汉式”,因为只有需要的时候才创建,比较懒,正如我们大学学习的时候,只有要考试的时候才会去看课本。
还有一种方式,我们称之为“饿汉式”,具体的代码如下:
package com.baidu.test;
public class One {
private static One one = new One();
private One() {}
public static One getInstance() {
return one;
}
}
因为One这个类的实例是一开始就创建了,比较“饥渴”, 所以称之为“饿汉式”。与懒汉式比起来这里的代码更加的简单,也很容易理解。但是有点不太好的就是,如果这个类我们程序之中并没有用到,而且创建很耗费时间和资源,那对我们开发程序是有一定的影响的。
其实,说到这里,对于上面的大家都知道,单例模式是所有设计模式中最简单的模式,如果仅仅说这么多这篇文章是没有必要的,下面进入今天的重点部分。
对我们上面说道的“懒汉式”,如果在多线程中使用的时候,是否会出现点什么问题呢?请看下面的执行片段:
//A线程执行
public static One getInstance() {
</pre><pre name="code" class="cpp">//B线程可开始执行
public static One getInstance() {
</pre><pre name="code" class="cpp"><pre name="code" class="java">//A线程执行
if (null == one) {
</pre><pre name="code" class="cpp">//B线程执行
if (null == one) {
//A线程执行
one = new One();
//B线程执行
one = new One();
就像上面那样交替的执行,这时候我们就会惊奇的发现,产生了2个实例对象。这难道还是我们所要的单例类么?答案是否定的。
聪明的你,肯定会想到我可以在getInstance() 方法前面加上 同步synchronized ,这样就解决了产生多个实例的问题,OK, 这种方法绝对是没有问题的,但是细心的你马上又会发现,如果这样似乎对我们的性能有些影响,比如多个线程想得到这个实例读取数据的时候,现在只能一个一个的进行了,不过如果你的程序对于这些额外的负担是可以承受的,那就完全可以这么实现,但是,如果考虑到刚刚我们所说的那种情况呢?
这个时候,迫使我们开始思考,怎样既能实现单例又能考虑性能?
它是被设计用来修饰被不同线程访问和修改的变量
百度百科上对 volatile 的解释是上面;(注:此关键字是在1.5版本开出现的,所以,1.5之前的版本并不能使用下面的方法)
package com.baidu.test;
public class One {
private volatile static One one = null;
private One() {}
public static One getInstance() {
if (one == null) {
synchronized (One.class) {
if (one == null) {
one = new One();
}
}
}
return one;
}
}
使用上面的做法可以大大减少getInstance()所耗费的时间。如果你考虑的是性能,这无疑正是你所需。
其实上面的代码就是控制了类实例化的时候加同步,在使用的时候就和我们上面说到的懒汉式一样了,是不是一个很愉快的解决方案?
到这里,今天的主题多线程中的单例就结束了!