单例模式

-
非线程安全
image.png
-
加锁: 函数结束自动释放锁
- 线程安全
-
有instance之后就没必要加锁了。但是这种实现每次进来都要加锁
image.png
-
双检查锁
- 不能去掉49行
- 去掉,当有thred1,thred2同时进入48行后,虽然thred1先执行48行,50行了,但是当thred1退出后,thred2获得锁之后,还是会执行50行。又申请了一个对象
- 问题:内存读写reorder不安全。所以双检查锁不能用,它并不正确
- 50行正常执行顺序是:分配内存、初始化,返回指针;但是 编译器优化 后顺序很可能是: 分配内存、返回指针、初始化。会导致thred1得到了指针,还没有初始化时,thred2执行了47行,导致直接返回instance。但是此时并没有初始化
image.png
- 50行正常执行顺序是:分配内存、初始化,返回指针;但是 编译器优化 后顺序很可能是: 分配内存、返回指针、初始化。会导致thred1得到了指针,还没有初始化时,thred2执行了47行,导致直接返回instance。但是此时并没有初始化
- 不能去掉49行
-
双检查锁-避免reorder: 正确
- 保证74行,编译器不会对其
reorder
- 取变量都用
load
来取
image.png
- 保证74行,编译器不会对其
参考
另一个角度
懒汉模式
实例能晚一点构造就晚一点构造的思想,直到第一次使用单例时才构造单例。https://blog.csdn.net/qq_35280514/article/details/70211845
- meyer's singleton
- 98及之前不多线程安全。可能会多个线程都初始化local static
- 11安全。保证了local static会初始化完成
class Singleton
{
public:
static Singleton& Instance()
{
static Singleton singleton;
return singleton;
}
private:
Singleton() { };
};
- 加锁
- 双检查锁
- 更正版的双检查锁
饿汉模式
即使实例永远不会被使用,实例的构造还是会早早的发生。 http://songlee24.github.io/2014/03/11/singleton-pattern/
- 懒汉模式instance在函数内部声明的
- 饿汉模式instance是成员变量
// version 1.3
class Singleton
{
private:
static Singleton instance;
private:
Singleton();
~Singleton();
Singleton(const Singleton&);
Singleton& operator=(const Singleton&);
public:
static Singleton& getInstance() {
return instance;
}
}
// initialize defaultly
Singleton Singleton::instance;
- 由于在main函数之前初始化,所以没有线程安全的问题。但是潜在问题在于no-local static对象(函数外的static对象)在不同编译单元中的初始化顺序是未定义的。如果在初始化完成之前调用 getInstance() 方法会返回一个未定义的实例。
简单工厂
创建对象时不给用户暴露内部细节,提供一个创建对象的通用接口
工厂方法
解决问题
-
不要“依赖具体类”,不要编译时具体依赖
面向接口编程

朴素想法


- 还是编译时具体依赖
factory抽象




抽象工厂
提供一个接口,负责创建一系列“相关、依赖的对象”,无需指定他们具体的类



适配器模式
将一个接口转换成客户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)
- 通过
继承
、组合
实现 - 例子: stl中用deque实现stack和queue
class Adaptee
{
//被适配的,也就是已有的,老的
public:
Adaptee();
virtual ~Adaptee();
void specificRequest();
};
class Adapter : public Target //新功能类
{
// 进行适配
public:
Adapter(Adaptee *adaptee); // 1. 接受被适配的东西,就是已有的功能
virtual ~Adapter();
virtual void request(); //2. 需要实现的新功能
private:
Adaptee* m_pAdaptee;
};
int main(int argc, char *argv[])
{
Adaptee * adaptee = new Adaptee();//3. 调用
Target * tar = new Adapter(adaptee);
tar->request();
return 0;
}
观察者模式
建立一种对象与对象之间的依赖关系,一个对象发生改变时将自动通知其他对象,其他对象将相应做出反应。在此,发生改变的对象称为观察目标,而被通知的对象称为观察者,一个观察目标可以对应多个观察者,而且这些观察者之间没有相互联系,可以根据需要增加和删除观察者,使得系统更易于扩展,这就是观察者模式的模式动机。
abstract class Subject {
private Vector obs = new Vector();
public void addObserver(Observer obs){
this.obs.add(obs);
}
public void delObserver(Observer obs){
this.obs.remove(obs);
}
protected void notifyObserver(){ // 1. 继承自subject就能使用通知所有observer了
for(Observer o: obs){
o.update();
}
}
public abstract void doSomething();
}
class ConcreteSubject extends Subject {
public void doSomething(){
System.out.println("被观察者事件反生");
this.notifyObserver();
}
}
interface Observer { // 2. 继承自observer,在update里面更新就能被通知到
public void update();
}
class ConcreteObserver1 implements Observer {
public void update() {
System.out.println("观察者1收到信息,并进行处理。");
}
}
class ConcreteObserver2 implements Observer {
public void update() {
System.out.println("观察者2收到信息,并进行处理。");
}
}
public class Client {
public static void main(String[] args){
Subject sub = new ConcreteSubject();
sub.addObserver(new ConcreteObserver1()); //添加观察者1
sub.addObserver(new ConcreteObserver2()); //添加观察者2
sub.doSomething();
}
}
桥模式
将抽象部分与它的实现部分分离,使它们都可以独立地变化 http://www.jasongj.com/design_pattern/bridge/
- 强调组合
网友评论