本文共 2132 字,大约阅读时间需要 7 分钟。
单例模式在多线程下的多种实现模式
单例模式是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修饰的成员变量在每次被线程访问时, 都强迫 从共享内存中 重读 该成员变量的值。//而且,当成员变量发生变化时,强迫 线程将变化值 回写到共享内存。这样在任何时刻,//两个不同的线程总是看到某个成员变量的同一个值。转载于:https://blog.51cto.com/12525470/2074318