欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

设计模式 | 单例模式(singleton)

程序员文章站 2022-03-22 12:03:48
定义: 定义: 保证一个类仅有一个实例,并提供一个访问它的全局访问点。 结构:(书中图,侵删) 结构:(书中图,侵删) 结构超简单。就在你想控制实例数的类上操作即可。 1.定义一个static的对象引用 2.私有化构造方法 3.提供一个获取实例的方法(static的) 实例: 这又是一个在面试中频繁 ......

定义:

保证一个类仅有一个实例,并提供一个访问它的全局访问点。

结构:(书中图,侵删)

设计模式 | 单例模式(singleton)
结构超简单。就在你想控制实例数的类上操作即可。
1.定义一个static的对象引用
2.私有化构造方法
3.提供一个获取实例的方法(static的)
 

实例:

这又是一个在面试中频繁出现的设计模式,我至今不知道为什么大家那么偏爱这个模式。
而且基本上都是让你现场写一个单例模式的例子,基于这个很现实的原因,这个模式也好好好掌握。
我这里就不举生活中的例子了,直接上代码。
 
单例模式又分为两种形式:
一、饿汉式:在类加载是就初始化实例。
二、懒汉式:在第一次调用的时候,才初始化实例。
 
饿汉式:
package designpattern.singleton;

public class hungrysingleton {
    private static hungrysingleton instance = new hungrysingleton();// 静态初始化

    private hungrysingleton() {// 私有化构造方法
    }

    public static hungrysingleton getinstance() {// 获取实例,static的
        return instance;
    }
}
懒汉式:
package designpattern.singleton;

public class lazysingleton {
    private static lazysingleton instance;

    private lazysingleton() {// 私有化构造方法

    }

    public static lazysingleton getinstance() {// 获取实例,static的
        if (instance == null) {
            instance = new lazysingleton();// 方法中创造实例
        }
        return instance;
    }
}
但是,如果是在多线程的情况下,可能会造成创造出两个实例的情况,可以考虑在getinstance方法上加上synchronized修饰:
package designpattern.singleton;

public class lazysingleton2 {
    private static lazysingleton2 instance;

    private lazysingleton2() {

    }

    public static synchronized lazysingleton2 getinstance() {// synchronized 修饰
        if (instance == null) {
            instance = new lazysingleton2();
        }
        return instance;
    }
}
这个可以进一步优化,只让线程在还没有实例化的情况下加锁:
package designpattern.singleton;

import java.util.concurrent.locks.lock;
import java.util.concurrent.locks.reentrantlock;

public class lazysingleton3 {
    private static lazysingleton3 instance;
    private static lock lock = new reentrantlock();

    private lazysingleton3() {

    }

    public static synchronized lazysingleton3 getinstance() {
        try {
            if (instance == null) {
                lock.lock();
                if (instance == null) {// 有必要再次判断,不然还是存在线程安全问题
                    instance = new lazysingleton3();
                }
                lock.unlock();
            }
        } finally {// 保证锁被释放
            lock.unlock();
        }
        return instance;
    }
}

总结:

饿汉式,在类加载是就初始化实例,要提前占用系统资源,但是不用考虑多线程访问可能造成的创建多个实例的问题。
懒汉式,在第一次调用的时候,才初始化实例,不用提前占用系统资源,但是需要考虑到多线程访问的问题。
这个模式在所有需要控制实例数的情况下都能使用,最常见的两个例子,就是数据库连接池和线程池。
以下原因引用自《单例模式的常见应用场景》:
数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。数据库软件系统中使用数据库连接池,主要是节省打开或者关闭数据库连接所引起的效率损耗,这种效率上的损耗还是非常昂贵的,因为何用单例模式来维护,就可以大大降低这种损耗。