单例模式在多线程下的多种实现模式
单例模式是23种设计模式中比较常见的设计模式,又因为其代码量精简,所以经常会被用在在面试中测试面试者的能力。

初级的单例模式很简单

实现两个要求

1构造方法私有化

2对外提供静态的,公开的获取对象的方法

所以:初级单例模式如下

public class Singelton {
private Singelton(){}
private static Singelton sin=null;
public static Singelton getSingelton(){
if(sin==null){
sin=new Singelton();
}
return sin;
}

}


但是这样就够了吗?

随着学习的深入,我们知道程序大多数在多线程的环境下运行。这就对我们的代码提出了更高质量的要求,要求我们考虑线程安全问题。

仅仅是上面的那段代码无法保证线程的安全。于是:

public class SingletonThread {
private SingletonThread() {}
private static SingletonThread st=null;
public synchronized static SingletonThread getSingletonThread(){
if(st==null){
st=new SingletonThread();
}
return st;
}

}

这段代码考虑到了线程安全,但是,在方法上加锁代价是否太大了?效率与单线程相近,假设这个方法中有上万行代码,在方法上加锁

是很不划算的。

所以,我们有更好的方法

1

public final class SingletonOne {//饿汉式,不能实现按需分配
private SingletonOne(){};
private static SingletonOne sin=new SingletonOne();
public static SingletonOne getSingleton(){
return sin;
}
}

利用静态成员仅在类加载阶段执行一次的性质,得到唯一的对象。

此方法不仅线程安全,而且方法简介。

2我们能否不一开始就创建类的实例呢?做到按需分配

如下:

public final class SingletonTwo {
private SingletonTwo(){};
public static SingletonTwo setsin(){
return singleton.sin;
}
static class singleton{//内部类不会再外部类加载时加载,故此是按需分配。
private singleton() {};
private static final SingletonTwo sin=new SingletonTwo();

}
}

利用内部类不会在外部类加载时被加载的性质,真正实现了按需分配。


以上两种方法是极好的,但是也需要根据实际情况使用,因为类中的方法和属性都是静态的,即使被继承之后也会被隐藏,

不能通过重写来实现多态,已经失去了被继承的意义,故此还有另一种推荐方法:

3

public class SingletonV {
private SingletonV(){}
private volatile SingletonV singleton=null;
public SingletonV getSingleton(){
if(singleton==null){
synchronized (SingletonV.class) {
if(singleton==null){
singleton=new SingletonV();
}
}
}
return singleton;
}
}

这样类还保留了继承的意义,同样要加锁,但是开销小得多。利用了关键字volatile。

具体用法如下

//java内存模型规定所有的变量都是存在主存当中(类似于前面说的物理内存),
//每个线程都有自己的工作内存(类似于前面的高速缓存)。
//线程对变量的所有操作都必须在工作内存中进行,
//而不能直接对主存进行操作。并且每个线程不能访问其他线程的工作内存。
//这就可能造成一个线程在主存中修改了一个变量的值,
//而另外一个线程还继续使用它在自己工作内存中的变量值的拷贝,造成数据的不一致。
//要解决这个问题,把该变量声明为volatile(不稳定的)即可,
//这就指示JVM,这个变量是不稳定的, 每次使用它都到 主存中 进行读取。
//一般说来,多任务环境下各任务间共享的标志都应该加volatile修饰。
//Volatile修饰的成员变量在每次被线程访问时, 都强迫 从共享内存中 重读 该成员变量的值。
//而且,当成员变量发生变化时,强迫 线程将变化值 回写到共享内存。这样在任何时刻,
//两个不同的线程总是看到某个成员变量的同一个值。