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