单例模式简介
单例模式是一种设计模式,它保证一个类仅有一个实例,并提供一个全局访问点,在PHP中,我们可以通过以下几种方式实现单例模式:
1、懒汉式(线程安全)
2、饿汉式
3、双重检查锁定(DCL)
4、静态内部类
懒汉式(线程安全)
懒汉式单例模式是在第一次请求时实例化对象,这种方式简单易懂,但可能会导致线程安全问题,下面是一个简单的懒汉式单例模式实现:
class Singleton { private static $instance; private function __construct() {} public static function getInstance() { if (!self::$instance) { self::$instance = new self(); } return self::$instance; } }
饿汉式
饿汉式单例模式是在程序启动时就实例化对象,这样可以避免多线程环境下的线程安全问题,由于对象在程序启动时就已经创建,可能导致资源浪费,下面是一个简单的饿汉式单例模式实现:
class Singleton { private static $instance = new self(); private function __construct() {} public static function getInstance() { return self::$instance; } }
双重检查锁定(DCL)
双重检查锁定是一种线程安全的单例模式实现方式,它在懒汉式的基础上增加了一层检查锁定机制,确保只有一个实例被创建,下面是一个简单的双重检查锁定单例模式实现:
class Singleton { private static $instance = null; private static $lock = false; private function __construct() {} public static function getInstance() { if (!self::$lock && null === self::$instance) { self::$lock = true; self::$instance = new self(); } elseif (self::$lock && null !== self::$instance) { self::$instance->__clone(); // 防止克隆导致的内存泄漏问题 } elseif (null !== self::$instance) { // 防止多个非线程安全的getInstance()调用同时执行到这一句,导致实例重复创建或未创建的情况发生,注意这里不能使用++操作符,因为它不是原子操作,这个判断条件要放在最前面,否则在多线程环境下可能会出现问题,具体请参考下面的示例代码,为了防止循环引用导致内存泄漏,可以使用SplObjectStorage来存储已经创建的实例,下面是一个简单的双重检查锁定单例模式实现:
class SplSingleton implements \IteratorAggregate, \ArrayAccess, \Countable, Serializable{//接口定义就不写了,具体可自行查阅相关资料,这里需要注意的是,如果要让SplSingleton支持序列化和反序列化操作,那么需要重载Serializable接口的serialize()和unserialize()方法,由于实现了IteratorAggregate、ArrayAccess、Countable三个接口,所以也可以使用foreach、offsetExists、count等数组相关的函数,最后是iterator(),这个函数返回一个迭代器对象,用于遍历SplSingleton中的元素,下面是一个简单的双重检查锁定单例模式实现:function __construct(){//构造函数私有化,防止外部直接创建实例,if(null===$this->instance){//加锁判断是否已经创建过实例if(!static::$lock){//加锁static::$lock=true;//加锁操作try{//加锁执行创建实例的操作$this->instance=new static();}finally{//无论是否成功都释放锁static::$lock=false;}}}}else{//如果已经存在实例,则克隆一份新的实例给当前实例$this->instance=$this->instance->__clone();}}public function __clone(){//禁止克隆function __clone(){throw new \Exception('不允许克隆');}}public function rewind(){//重置指针function rewind(){throw new \Exception('不允许重置指针');}}public function current(){//获取当前元素function current(){return $this->instance;}}public function key(){//获取键名function key(){return null;}}public function next(){//获取下一个元素function next(){throw new \Exception('不允许获取下一个元素');}}public function valid(){//判断是否还有下一个元素function valid(){return true;}}public function getIterator(){//返回迭代器对象return new ArrayIterator([$this->instance]);}}?>```
原创文章,作者:K-seo,如若转载,请注明出处:https://www.kdun.cn/ask/260371.html