java多线程 —— 单例模式

目录

1、什么是单例?

2、为什么使用单例?

3、单例模式怎么创建


1、什么是单例?

    单例模式实际上是一种设计模式。它达到的效果是,在程序的运行过程中,一个类最多只会被实例化(初始化)一次。

2、为什么使用单例?

    上面我们已经知道单例模式,其实就是一个类的变量和方法最多只会被初始化一次,即全局唯一。全局唯一可以节省内存开销,提供全局唯一的变量和方法等等优点,但是其实静态变量和静态方法也是能达到全局唯一的效果,那么为什么一定要用单例呢?

    其实,在我看来,单例可以达到的效果也可以用静态变量和静态方法的方式而实现。单例模式的存在意义,有两点:

  1. 面向对象的编程方式。何为面向对象:https://blog.csdn.net/jjs15259655776/article/details/77507529
  2. 可以控制实例的数量,进行有意义的派生。
  3. 多线程的情况下,如果静态方法使用了一个静态字段,这个静态字段可以会被多个线程修改,因此说如果在静态方法里使用了静态变量,这就会有线程安全问题

    可以这么说,静态变量和静态方法的方式是基于对象,单例模式是面向对象的。

例如

  1. 数据库连接池。数据库连接池不仅有连接池的配置、属性,还有获取连接的各种方法。显然,这是一种面向对象的编程方式。又因为数据库连接池在大多数设计上面都是全局唯一的,所以数据库连接池经常被设计为单例。
  2. 系统运行时的配置、属性、方式。这种场景也经常设置为单例。
  3. 一些工具类,提供公共的方法实现一些特定的功能,这时候就可以用静态方法。

3、单例模式怎么创建

    由于大部分(有价值的)程序都是多线程的。我这边只提供线程安全的单例模式,当然线程不安全的单例模式其实是错误的单例模式。

  1. 饿汉模式:
  • 即利用静态变量的特点,在类加载的时候就初始化SingleTon1,以达到线程安全的单例模型。
  • 但是,没有懒加载的效果,如果你的单例是比较大的,不建议使用饿汉模式。
/**
 * 这种叫饿汉模式。
 * 即利用静态变量的特点,在类加载的时候就初始化SingleTon1,已达到线程安全的单例模型。
 */
public class SingleTon1 {

    private static SingleTon1 instance = new SingleTon1();//利用静态变量的特点,在类加载的时候就初始化SingleTon1

    private SingleTon1() {//私有化构造函数,这样在其他类就不能够初始化类
    }

    public static SingleTon1 getInstance() {//获取的是同一个instance
        return instance;
    }

}
public class Singleton {  
    private Singleton instance = null;  
    static {  
    instance = new Singleton();  //利用静态代码块,在类加载的时候初始化,其实跟上面是一样的,算是一种饿汉模式的变种
    }  
    private Singleton (){}  
    public static Singleton getInstance() {  
    return this.instance;  
    }  
}  

2、双重检验锁

  • 达到懒加载的效果,而不是类加载过程中就初始化,达到节省内存的效果
  • volatile。用volatile修饰instance是必要的,不修饰的话,有可能因为延迟初始化的问题,导致系统崩溃。具体的,是因为java的初始化是无序的,如果不用volatile修饰instance,保证instance的可见性,有可能一个线程得到的instance是没有初始化完成的。即一个线程拿到的是还会初始化的对象,这时候会造成系统崩溃。
  • 双重检验null的原因。一个原因是提升效率,一个是达到线程安全的目的。具体的需要结合代码一步一步看,这里就不展开了。

public class SingleTon2 {
    private static volatile SingleTon2 instance;
    private SingleTon2(){}
    public static SingleTon2 getInstance(){
        if (instance == null){
            synchronized (SingleTon2.class){
                instance = new SingleTon2();
            }
        }
        return instance;
    }
}

这里值得一提的是枚举的方式实现单例。

  • 枚举的方式除了能保证线程安全以外,还有一个其他实现方式无法比拟的有点,就是枚举的实现方式支持序列化和反序列化
package singleTon;


public enum SingleTon3 {
    INSTANCE;
    public void test(){
        System.out.println("枚举型单例");
    }
}

class Test{
    public static void main(String[] args) {
        SingleTon3.INSTANCE.test();
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章