直接用__new__实现单例不线程安全,因多线程可能同时通过if cls._instance is None判断并创建实例;必须用双重检查加锁确保“检查+创建+赋值”原子化,并在__init__中加初始化守卫防止重复初始化。

为什么直接用 __new__ 实现单例不线程安全
因为多个线程可能同时执行到 if cls._instance is None: 这一行,都判断为 True,然后各自调用 super().__new__(cls) 创建新实例,最终覆盖彼此。这不是“偶尔出错”,而是在高并发下必然发生——只要没加锁,就不是线程安全的。
必须加锁:用 threading.Lock 包裹实例创建逻辑
不能只锁判断,也不能只锁返回,得把「检查 + 创建 + 赋值」整个过程包进锁里。常见错误是锁粒度太粗(比如锁住整个 __new__ 方法体但没考虑已存在实例时的性能损耗),或太细(漏掉赋值步骤)。
推荐写法:
import threading
<p>class Singleton:
_instance = None
_lock = threading.Lock()</p><pre class="brush:php;toolbar:false;">def __new__(cls):
if cls._instance is None:
with cls._lock:
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
立即学习“Python免费学习笔记(深入)”;
- 外层
if是快速失败(避免每次都要抢锁) - 内层
if是双重检查(防止多个线程同时通过外层判断后重复创建) -
with cls._lock确保创建和赋值原子化
__new__ 里不能调用 __init__ 的副作用
单例对象只应初始化一次,但 Python 默认每次调用 __new__ 后都会触发 __init__。如果用户写了带状态的 __init__,会导致多次执行,破坏单例语义。
解决方法是在类中加标记:
def __new__(cls):
if cls._instance is None:
with cls._lock:
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance._initialized = False
return cls._instance
<p>def <strong>init</strong>(self):
if self._initialized:
return</p><h1>初始化逻辑放这里</h1><pre class="brush:php;toolbar:false;">self._initialized = True
立即学习“Python免费学习笔记(深入)”;
- 不要在
__new__里手动调用__init__ - 用实例属性(如
_initialized)控制__init__执行次数 - 注意:
__init__总是会被调用,必须在里面做守卫
Python 3.7+ 可考虑 threading.local?不,那是线程局部存储,不是单例
有人混淆 threading.local 和单例:前者每个线程一个副本,后者全进程唯一。如果你要的是“每个线程一个单例”,那叫线程局部单例,需额外封装;但标准单例要求全局唯一,threading.local 完全不适用。
真正要注意的是:__new__ 的线程安全只保障实例创建,不保障后续方法调用。如果单例里有可变状态(如缓存字典),读写仍需单独加锁或用线程安全结构(如 queue.Queue、threading.RLock)。


















