首页 > 技术文章 > 设计模式----创建型设计模式【单例模式】ok

staff 2019-10-09 17:07 原文

 

单例模式的特点是有两个类(一个业务类构造方法私有,一个工厂类负责保存业务类的单例,注意多线程的使用)

 

 

 

 


 

Java中,单例模式有七种写法,其中第一种最简单,最常用,且线程安全。

 

方式一;饿汉式

package sington;

/**
 * 饿汉式
 * 类加载到内存后,就实例化一个单例,JVM保证线程安全
 * (JVM保证每个class只load到内存一次,
 * static是在class load到内存之后马上就初始化的,也只初始化一次,多线程访问都没关系)
 * 简单实用,推荐使用!
 * 唯一缺点:不管用到与否,类装载时就完成实例化
 * (话说你不用的,你装载它干啥)
 */
public class Mgr01 {
    private static Mgr01 INSTANCE = new Mgr01();
    private Mgr01(){}
    public static Mgr01 getInstance(){
        return INSTANCE;
    }
}

 

方式二:饿汉式,static代码块

package sington;

/**
 * 饿汉式
 * 跟第一种一样,只是new Instance放在static块中
 */

public class Mgr02 {
    private static final Mgr02 INSTANCE;
    static {
        INSTANCE = new Mgr02();
    }
    private Mgr02(){}
    public static Mgr02 getInstance(){
        return INSTANCE;
    }
}

 

方式三:懒加载,线程不安全

package sington;

/**
 * 懒汉式
 * 虽然实现了“要用到的时候才初始化”,但是会造成多线程访问时,获取到不是同一个对象的问题:
 * 两个线程都执行到“if (Instance == null)”语句块里面
 */

public class Mgr03 {
    private static Mgr03 INSTANCE;
    private Mgr03(){}
    public static Mgr03 getInstance(){
        if (INSTANCE == null){
            INSTANCE = new Mgr03();
        }
        return INSTANCE;
    }
}

 

方式四:synchronized方法

package sington;

/**
 * 懒汉式
 * 加synchronized 来解决“多线程访问时,获取到不是同一个对象”的问题, 但也造成了效率下降
 */

public class Mgr04 {
    private static Mgr04 INSTANCE;
    private Mgr04(){}
    public static synchronized Mgr04 getInstance(){
        if (INSTANCE == null){
            INSTANCE = new Mgr04();
        }
        return INSTANCE;
    }
}

 

方式五:synchronized代码块,双重检查

package sington;

/**
 * 懒汉式
 * 加锁,双重检查,多线程访问安全
 *
 * 要加volatile,防止指令重排序
 * INSTANCE = new Mgr05()分为三步
 * 1、给对象申请内存,成员变量为零值
 * 2、成员变量初始化值
 * 3、内存内容赋值给INSTANCE
 *
 * 如果不加volatile,指令重排序,会将第3步和第2步颠倒位置
 * 那么下一个线程可能在第2步将对象取走,并使用还未初始化的成员变量
 * 就会导致问题
 */
public class Mgr05 {
    private static volatile Mgr05 INSTANCE;
    private Mgr05(){}
    public static Mgr05 getInstance(){
        if (INSTANCE==null) {
            //双重检查
            synchronized (Mgr05.class){
                if (INSTANCE==null){
                    INSTANCE = new Mgr05();
                }
            }
        }
        return INSTANCE;
    }
}

 

 

方式六:使用静态内部类

package sington;

/**
 * 懒加载方式
 * 使用静态内部类来实现:
 * 不调用getInstance方法,不会加载Mgr06Holder类,不会执行里面的new Mgr06()方法
 */
public class Mgr06 {
    private Mgr06(){}
    private static class Mgr06Holder{
        private static final Mgr06 INSTANCE = new Mgr06();
    }
    public static Mgr06 getInstance(){
        return Mgr06Holder.INSTANCE;
    }
}

 

方式七:使用枚举类

package sington;

/**
 * 最完美的单例模式:使用枚举类实现
 * 而且不能通过反射创建对象
 */
public enum Mgr07 {
    INSTANCE;

    public static void main(String[] args) {
        System.out.println(Mgr07.INSTANCE.hashCode());
    }
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


 

 

 

方式一:使用模块

模块是天然的单例

# 如果已经导入的模块被重新导入,Python不会重新解释一遍
# 而是从内存中直接将原来导入的值拿来使用
使用这种机制,可以维护一个模块间的公共对象,所有模块都可以往这个对象里传值,再取出

==========

# 作为Python模块时是天然的单例模式

#创建一个sington.py文件,内容如下:
    class Singleton(object):
        def foo(self):
            pass
    mysington = Singleton()

# 运用
from sington import mysington   # 不论import多少次,mysington对象都是单例的
mysington.foo() 

==========

 

'''
把全局状态放在私有变量中
并提供用于访问此变量的公开函数
'''

def get(refresh=False):
    if refresh:
        get.rates = {}
    if get.rates:
        return get.rates
    get.rates['usd'] = 0.4  # 初始化,可以从URL更新
    return get.rates

get.rates = {}

'''
以上语句放入:Rates.py模块中
创建了字典rates并将其作为Rates.get函数的属性。
第一次执行:
import Rates
Rates.get()或者Rates.get(refresh=True)
时,会下载全新的汇率数据,其他时候只需把最近下载的那份数据返回就行了。
此处没有引入类,依然把汇率数据做成了单例的。
'''

 

方式二:重写__new__方法

class Singleton:
    def __new__(cls, *args, **kwargs):
        if not hasattr(cls, "_instance"):
            cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs)
        return cls._instance

class Foo(Singleton):  # 继承Singleton的类都是单例的,线程安全
    pass

f1 = Foo()
f2 = Foo()
f3 = Foo()
print(id(f1), id(f2), id(f3))

# 34879944 34879944 34879944

 

推荐阅读