单例模式

一、什么是单例模式
单例对象的类只允许一个实例存在。

主要理解:比如说在一个服务器程序之中,该服务器的所有配置信息都放在同一个文件之中,那么我们可以创建一个单例对象统一读取,服务器中其他的进程对象都只需要通过这一个单例对象即可获得想要获得的配置信息。这种方式简化了在复杂环境下的配置管理,有利于协调系统的整体行为。

二、单例模式是如何实现的
我们一般回想单例对象的类的构造器定义为私有的,这样其他处的代码就不能通过这个构造器来创建该类的实例了。然后在这个单例对象的类中我们会定义一个静态方法。当我们去调用这个静态方法的时候,如果该类的实例已经创建了,就会返回这个已经创建的实例,如果实例还没有创建,救会创建这个类的实例然后返回。

三、单例模式的运用场景
举如下几个例子:

1.比如说我们windows桌面中的回收站,我们最多只能打开一个回收站。我们是无法打开第二个回收站的,这就是一个单例模式的例子,系统只需要维护一个回收站的实例。为什么这里要运用单例模式呢,假如这里不使用单例模式,我们可以打开多个回收站,然而这些回收站的资源是共享的,打开多个回收站会造成系统不必要的负担,造成系统的资源浪费。

2.网站的计数器一般也是采用单例模式,如果存在多个计数器,每个用户都去刷新这个计数器的值,这样就很难做到同步计数器的值。

3.多线程的线程池的设计一般也是采用单例模式,这是因为线程池需要方便对池中线程进行控制。

4.httpapplication四大域中的application域也是单例模式的典型

综上运用场景主要有如下四点:

1.需要生成唯一序列的环境
2.需要频繁实例化然后销毁的对象。
3.创建对象时耗时过多或者耗资源过多,但又经常用到的对象。
4.方便资源相互通信的环境

四、单例模式的优缺点
优点:

1.内存中只用创建一个对象,节省内存空间

2.避免频繁的创建和销毁对象,可以提高性能

3.避免对共享资源的多重占用,简化访问

4.为整个系统提供一个全局访问点。

缺点:

  1. 不适用于变化频繁的对象;

2.滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出

3.如果实例化的对象长时间不被利用,系统会认为该对象是垃圾而被回收,这可能会导致对象状态的丢失

五、单例模式的实现
1.饿汉式
1
2
3
4
5
6
7
8
9
10
11
12
13
public class Singleton1 {
// 主动创建自己的私有静态引用
private static Singleton1 singleton1 = new Singleton1();

//私有化构造器
private Singleton1(){
     
}
//创建公共静态方法返回自己的唯一实例
public static Singleton1 getSingleton1(){
    return singleton1;
}

}
  

优点:这种写法比较简单,就是在类装载的时候就完成实例化。避免了线程同步问题。

缺点:在类装载的时候就完成实例化,没有达到Lazy Loading的效果。如果从始至终从未使用过这个实例,则会造成内存的浪费。

2.懒汉式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Singleton2 {
private static Singleton2 singleton2;

private Singleton2(){
     
}
 
public static Singleton2 getSingleton2(){
    if(singleton2!=null){
        singleton2 = new Singleton2();
    }
    return singleton2;
}

}
  

这种方式,不同于饿汉式类加载了就会创建实例,懒汉式只有被真正用到的时候才会创建实例。这样就起到了懒加载,但是这样线程不安全,还未创建singleton的情况下,一个线程进入了条件判断(singleton==null),还未往下执行的情况下,另外一个线程正好抢先创建好了singleton的实例。这样这个线程还在线程内,因此它又会创建一个实例,这样会造成线程不安全。

3.双重加锁机制
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Singleton {
private static Singleton singleton;

private static Object sync = new Object();

private Singleton(){}

public static Singleton getInstance(){
    if(singleton!=null){
        synchronized (sync){
            if(singleton!=null){
                singleton = new Singleton();
            }
        }
    }
    return singleton;
}

}
http://www.zgswcn.com/article/202012/202012281253301142.html
http://www.zgswcn.com/article/202012/202012251624131222.html
http://www.zgswcn.com/article/202012/202012251539471203.html
http://www.zgswcn.com/article/202012/202012192152571037.html  

采用这种双重检测其实说白了就是加了一个锁,延迟加载的方式不但保证单例,而且还提高了程序的运行效率。

优点:线程安全,延迟加载,效率较高

六、总结
要想实现效率高的线程安全的单例,我们必须注意以下两点:

尽量减少同步块的作用域;

尽量使用细粒度的锁。

posted @ 2021-01-23 17:00  京落  阅读(84)  评论(0)    收藏  举报