• 单例模式在多线程下的多种实现模式


    单例模式是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修饰的成员变量在每次被线程访问时, 都强迫 从共享内存中 重读 该成员变量的值。
    //而且,当成员变量发生变化时,强迫 线程将变化值 回写到共享内存。这样在任何时刻,
    //两个不同的线程总是看到某个成员变量的同一个值。

  • 相关阅读:
    JSON.parse()和JSON.stringify()
    php结合layui实现前台加后台操作
    微信拨打电话功能
    视觉差效果
    前端开发面试题
    字符串分割--java中String.split()用法
    vue.js实现购物车功能
    localStorage使用总结
    canvas 实现赛车小游戏
    canvas 实现飞碟射击游戏
  • 原文地址:https://www.cnblogs.com/df-happyforever/p/6517009.html
Copyright © 2020-2023  润新知