猿思考系列3——一文学会思考的正确姿势

时间:2022-07-22
本文章向大家介绍猿思考系列3——一文学会思考的正确姿势,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

看完上一个章节,相信你已经掌握了一些编写并发代码编写的要领了。今天我们来聊一个新的话题。有童鞋反映说之前的两篇思考,内容有些深了,不是太好理解,猿人工厂君决定调整下思路,换个简约点的话题,希望简约而不简单。另外真的很感谢大家的支持,和巨兽的斗争暂时进入僵持阶段,猿人工厂君已经说了,虽千万人,吾往矣。中间细节,猿人工厂君,会在方便的时候公开,程序猿鸭,且行且珍惜。

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

单例模式的写法又分为好几种:懒汉模式、饿汉模式、双重锁模式、静态内部类单例模式、枚举模式。接下来就给你show一下各种写法。

 package com.pz.se.demo;
 
public class LanHanSingleton {
 
    private static LanHanSingleton instance;
    private LanHanSingleton (){}
 
    public static LanHanSingleton getInstance() {
        if(instance == null) {
           instance = new LanHanSingleton();
        }
        returninstance;
    }
}

上面这种写法就是懒汉单例模式,特点是类构造器私有,持有自己类型的属性,对外提供获取实例的静态方法,但是线程不安全,延迟初始化,严格意义上讲不算是单例模式。

package com.pz.se.demo;
 
public class EHanSingleton {
 
    private static EHanSingleton instance = new EHanSingleton();
    private EHanSingleton (){}
    public static EHanSingleton getInstance() {
        returninstance;
    }
}

上面的写法就是饿汉单例模式,因为一开始就初始化了,虽然线程安全了,但是可能产生垃圾。

package com.pz.se.demo;
 
public class SyncSingleton {
 
    private volatile static SyncSingleton singleton;
    private SyncSingleton (){}
    public static SyncSingleton getSingleton() {
        if(singleton == null) {
           synchronized (SyncSingleton.class) {
               if (singleton == null) {
                   singleton = new SyncSingleton();
               }
            }
        }
        return singleton;
    }
}

上面的写法就是双重锁单例模式,这种写法,线程安全,延迟了实例的初始化。这种方式采用双锁机制,线程安全而且在多线程情况下能保持较好的性能。

 
package com.pz.se.demo;
 
public class StaticInnerSingleton {
 
    private StaticInnerSingleton(){
    }
    public static StaticInnerSingleton getInstance(){
        return Inner.instance;
    }
    private static class Inner {
       private static final StaticInnerSingleton instance = newStaticInnerSingleton();
    }
}

上面这种写法就是静态内部类单例模式,只有在第一次调用公共方法时,JVM才会加载内部内,完成类的初始化化时,得到一个静态对象,供大家使用,这种方式比较常见,也比较推荐。

package com.pz.se.demo;
 
publicenum EnumSingleton {
    INSTANCE;
 
    public static EnumSingleton getInstance() {
        return EnumSingleton.INSTANCE;
    }
}

上面这种写法就枚举模式,枚举类的方式默认就是线程安全的,隐藏了私有的构造器,在《EffectiveJava》这本书中是推荐写法,不过这样写代码的可读性不是很好。

关于为什么要使用单例模式,很多文章都是这些个观点:一是,解决多线程并发访问的问题。二是节约系统内存,提交系统运行的效率,提高系统性能。然后再扯上static作用域讲个半天。最后得到了千篇一律的解释,然鹅儿这似乎并没有啥卵用,而且很晦涩。

我们回到根本上来,从原理去分析这个问题。一个类由什么组成呢?属性和方法对吧。属性是什么?是变量,是引用,是数据的载体。方法呢?方法其实就是具备一段功能的算法。不要扯经典的数据结构和算法,算法就是解决问题的办法,具有某个功能的方法,他也是算法。那么类这个东西,从结构上讲,分为数据,和算法。

我们从java的内存模型上看,属性承载的数据,在堆内存,方法是在栈内存,而且你要是看了之前讲代码执行套路的文章,那么方法在执行的时候是多线程的方式来执行的。我们在使用方法的时候,很多时候用的是局部变量,没有用类变量,这本身就是为安全性的考虑。

回到为什么用单例模式这个问题上来,答案就很简单了。因为,在这个场景下,我只用使用类的算法。算法支持多线程,已经达到性能要求了。那为什么还有一些配置属性?其实,是当常量来用了,因为,根本就不会改变的情况下,才使用单例。

不信,你翻开你的代码,尤其是用了框架的,你的xxxService放着xxxManager,xxxMabager放着xxxDao,好多就是调用了方法,所以的使用方式是单例的方式,因为你其实只用了类的算法,已经可以看作是常量的数据!