diff --git "a/golang \351\253\230\347\272\247\347\274\226\347\250\213/.sync.Map\345\256\236\347\216\260.assets/20200505120255-16709237251825.png" "b/golang \351\253\230\347\272\247\347\274\226\347\250\213/.sync.Map\345\256\236\347\216\260.assets/20200505120255-16709237251825.png" new file mode 100644 index 0000000..3bbbad4 Binary files /dev/null and "b/golang \351\253\230\347\272\247\347\274\226\347\250\213/.sync.Map\345\256\236\347\216\260.assets/20200505120255-16709237251825.png" differ diff --git "a/golang \351\253\230\347\272\247\347\274\226\347\250\213/sync.Map\345\256\236\347\216\260.md" "b/golang \351\253\230\347\272\247\347\274\226\347\250\213/sync.Map\345\256\236\347\216\260.md" new file mode 100644 index 0000000..f9d62e8 --- /dev/null +++ "b/golang \351\253\230\347\272\247\347\274\226\347\250\213/sync.Map\345\256\236\347\216\260.md" @@ -0,0 +1,60 @@ +# Go sync.Map + +go map 不是线程安全。我们可以通过加锁去实现并发安全,但是对整个 map 加锁粒度太大。所以 Go 1.9 引入了 `sync.Map`。 + +## sync.Map 的结构 + +```go +// sync/map.go +type Map struct { + // 当写read map 或读写dirty map时 需要上锁 + mu Mutex + + // read map的 k v(entry) 是不变的,删除只是打标记,插入新key会加锁写到dirty中 + // 因此对read map的读取无需加锁 + read atomic.Value // 保存readOnly结构体 + + // dirty map 对dirty map的操作需要持有mu锁 + dirty map[interface{}]*entry + + // 当Load操作在read map中未找到,尝试从dirty中进行加载时(不管是否存在),misses+1 + // 当misses达到diry map len时,dirty被提升为read 并且重新分配dirty + misses int +} + +// read map数据结构 +type readOnly struct { + m map[interface{}]*entry + // 为true时代表dirty map中含有m中没有的元素 + amended bool +} + +type entry struct { + // 指向实际的interface{} + // p有三种状态: + // p == nil: 键值已经被删除,此时,m.dirty==nil 或 m.dirty[k]指向该entry + // p == expunged: 键值已经被删除, 此时, m.dirty!=nil 且 m.dirty不存在该键值 + // 其它情况代表实际interface{}地址 如果m.dirty!=nil 则 m.read[key] 和 m.dirty[key] 指向同一个entry + // 当删除key时,并不实际删除,先CAS entry.p为nil 等到每次dirty map创建时(dirty提升后的第一次新建Key),会将entry.p由nil CAS为expunged + p unsafe.Pointer // *interface{} +} +``` + +![sync.map 整体结构](.sync.Map实现.assets/20200505120255-16709237251825.png) + +## sync.Map 实现的原理 + +1、过 read 和 dirty 两个字段将读写分离,读的数据存在只读字段 read 上,将最新写入的数据则存在 dirty 字段上。 + +2、读取时会先查询 read,不存在再查询 dirty,写入时则只写入 dirty。 + +3、读取 read 并不需要加锁,而读或写 dirty 都需要加锁。 + +4、另外有 misses 字段来统计 read 被穿透的次数(被穿透指需要读 dirty 的情况),超过一定次数则将 dirty 数据同步到 read 上。 + +5、对于删除数据则直接通过标记来延迟删除。 + +## 参考文章 + +- [深度解密 Go 语言之 sync.map](https://qcrao.com/post/dive-into-go-sync-map/) +- [Go sync.Map 实现](https://wudaijun.com/2018/02/go-sync-map-implement/) \ No newline at end of file diff --git "a/\350\256\276\350\256\241\346\250\241\345\274\217/.\345\267\245\345\216\202\346\250\241\345\274\217.assets/\345\267\245\345\216\202\346\226\271\346\263\225.png" "b/\350\256\276\350\256\241\346\250\241\345\274\217/.\345\267\245\345\216\202\346\250\241\345\274\217.assets/\345\267\245\345\216\202\346\226\271\346\263\225.png" new file mode 100644 index 0000000..e7a0449 Binary files /dev/null and "b/\350\256\276\350\256\241\346\250\241\345\274\217/.\345\267\245\345\216\202\346\250\241\345\274\217.assets/\345\267\245\345\216\202\346\226\271\346\263\225.png" differ diff --git "a/\350\256\276\350\256\241\346\250\241\345\274\217/.\345\267\245\345\216\202\346\250\241\345\274\217.assets/\346\212\275\350\261\241\345\267\245\345\216\202\346\250\241\345\274\217UML\345\256\236\344\276\213\345\233\276.png" "b/\350\256\276\350\256\241\346\250\241\345\274\217/.\345\267\245\345\216\202\346\250\241\345\274\217.assets/\346\212\275\350\261\241\345\267\245\345\216\202\346\250\241\345\274\217UML\345\256\236\344\276\213\345\233\276.png" new file mode 100644 index 0000000..74f2225 Binary files /dev/null and "b/\350\256\276\350\256\241\346\250\241\345\274\217/.\345\267\245\345\216\202\346\250\241\345\274\217.assets/\346\212\275\350\261\241\345\267\245\345\216\202\346\250\241\345\274\217UML\345\256\236\344\276\213\345\233\276.png" differ diff --git "a/\350\256\276\350\256\241\346\250\241\345\274\217/.\345\267\245\345\216\202\346\250\241\345\274\217.assets/\347\256\200\345\215\225\345\267\245\345\216\202.png" "b/\350\256\276\350\256\241\346\250\241\345\274\217/.\345\267\245\345\216\202\346\250\241\345\274\217.assets/\347\256\200\345\215\225\345\267\245\345\216\202.png" new file mode 100644 index 0000000..ade0c34 Binary files /dev/null and "b/\350\256\276\350\256\241\346\250\241\345\274\217/.\345\267\245\345\216\202\346\250\241\345\274\217.assets/\347\256\200\345\215\225\345\267\245\345\216\202.png" differ diff --git "a/\350\256\276\350\256\241\346\250\241\345\274\217/\345\267\245\345\216\202\346\250\241\345\274\217.md" "b/\350\256\276\350\256\241\346\250\241\345\274\217/\345\267\245\345\216\202\346\250\241\345\274\217.md" index 7f76a9b..83244af 100644 --- "a/\350\256\276\350\256\241\346\250\241\345\274\217/\345\267\245\345\216\202\346\250\241\345\274\217.md" +++ "b/\350\256\276\350\256\241\346\250\241\345\274\217/\345\267\245\345\216\202\346\250\241\345\274\217.md" @@ -20,6 +20,8 @@ ## 简单工厂 +![简单工厂](.工厂模式.assets/简单工厂.png) + - **工厂(Factory)**:根据客户提供的具体产品类的参数,创建具体产品实例。 - **抽象产品(AbstractProduct)**:具体产品类的基类,包含创建产品的公共方法。 - **具体产品(ConcreteProduct)**:抽象产品的派生类,包含具体产品特有的实现方法,是简单工厂模式的创建目标。 @@ -106,6 +108,51 @@ int main() 的确如此,但是这明显违背了开闭原则(对扩展开放,对修改关闭),即在扩展功能时修改了既有的代码。另一方面,简单工厂模式所有的判断逻辑都在工厂类中实现,一旦工厂类设计故障,则整个系统都受之影响。 +## 工厂方法 + +简单工厂模式中,每新增一个具体产品,就需要修改工厂类内部的判断逻辑。为了不修改工厂类,遵循开闭原则,工厂方法模式中不再使用工厂类统一创建所有的具体产品,而是针对不同的产品设计了不同的工厂,每一个工厂只生产特定的产品。 + +![工厂方法](.工厂模式.assets/工厂方法.png) + +从工厂方法模式简介中,可以该模式有以下几种角色 + +- **抽象工厂(AbstractFactory)**:所有生产具体产品的工厂类的基类,提供工厂类的公共方法。 +- **具体工厂(ConcreteFactory)**:生产具体的产品工厂。 +- **抽象产品(AbstractProduct)**:所有产品的基类,提供产品类的公共方法。 +- **具体产品(ConcreteProduct)**:具体的产品类。 + +工厂方法模式 UML 类图如下: + +待补充 + + + +代码如下: + +待补充 + + + +如果要生产棒球(Baseball),只需要增加一个棒球工厂(Baseball Facory),然后在客户端代码中修改具体工厂类的类名,而原有的类的代码无需修改。由此可看到,相较简单工厂模式,工厂方法模式更加符合开闭原则。工厂方法是使用频率最高的设计模式之一,是很多开源框架和 API 类库的核心模式。 + +工厂方法优点: + +- 工厂方法用于创建客户所需产品,同时向客户隐藏某个具体产品类将被实例化的细节,用户只需关心所需产品对应的工厂。 +- 工厂自主决定创建何种产品,并且创建过程封装在具体工厂对象内部,多态性设计是工厂方法模式的关键。 +- 新加入产品时,无需修改原有代码,增强了系统的可扩展性,符合开闭原则。 + +工厂方法缺点: + +- 添加新产品时需要同时添加新的产品工厂,系统中类的数量成对增加,增加了系统的复杂度,更多的类需要编译和运行,增加了系统的额外开销; +- 工厂和产品都引入了抽象层,客户端代码中均使用的抽象层(AbstractFactory 和 AbstractSportProduct ),增加了系统的抽象层次和理解难度。 + +## 抽象工厂 + +![avatar](.工厂模式.assets/抽象工厂模式UML实例图.png) + ## 参考链接 -- [简单工厂模式简介](https://github.com/FengJungle/DesignPattern/blob/master/01.SimpleFactory/01.SimpleFactory.md) \ No newline at end of file +- [简单工厂模式简介](https://github.com/FengJungle/DesignPattern/blob/master/01.SimpleFactory/01.SimpleFactory.md) +- [工厂方法模式简介](https://github.com/FengJungle/DesignPattern/blob/master/02.FactoryMethod/02.FactoryMethod.md) + +- [抽象工厂模式简介](https://github.com/FengJungle/DesignPattern/blob/master/03.AbstractFactory/03.AbstractFactory.md) \ No newline at end of file