目录
1、什么是单例?
2、为什么使用单例?
3、单例模式怎么创建
1、什么是单例?
单例模式实际上是一种设计模式。它达到的效果是,在程序的运行过程中,一个类最多只会被实例化(初始化)一次。
2、为什么使用单例?
上面我们已经知道单例模式,其实就是一个类的变量和方法最多只会被初始化一次,即全局唯一。全局唯一可以节省内存开销,提供全局唯一的变量和方法等等优点,但是其实静态变量和静态方法也是能达到全局唯一的效果,那么为什么一定要用单例呢?
其实,在我看来,单例可以达到的效果也可以用静态变量和静态方法的方式而实现。单例模式的存在意义,有两点:
- 面向对象的编程方式。何为面向对象:
- 可以控制实例的数量,进行有意义的派生。
- 多线程的情况下,如果静态方法使用了一个静态字段,这个静态字段可以会被多个线程修改,因此说如果在静态方法里使用了静态变量,这就会有线程安全问题
可以这么说,静态变量和静态方法的方式是基于对象,单例模式是面向对象的。
例如
- 数据库连接池。数据库连接池不仅有连接池的配置、属性,还有获取连接的各种方法。显然,这是一种面向对象的编程方式。又因为数据库连接池在大多数设计上面都是全局唯一的,所以数据库连接池经常被设计为单例。
- 系统运行时的配置、属性、方式。这种场景也经常设置为单例。
- 一些工具类,提供公共的方法实现一些特定的功能,这时候就可以用静态方法。
3、单例模式怎么创建
由于大部分(有价值的)程序都是多线程的。我这边只提供线程安全的单例模式,当然线程不安全的单例模式其实是错误的单例模式。
- 饿汉模式:
- 即利用静态变量的特点,在类加载的时候就初始化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();
}
}
更多内容请关注微信公众号“外里科技”
官方公众号 | 外里科技 |
运营公众号 | 英雄赚 |
开源代码 |