1. 不變性(Immutability)模式
1.1. 不變性模式的概念
定義:對象一旦被創建,其內部狀態就不再發生變化,也即“只讀無寫”,不會出現并發寫的問題,自然線程安全。
適用場景:只讀共享數據場景,是并發編程中最簡單直接的線程安全保障方案。
不變性模式的作用:
不變性模式的實際作用不是為了“能不能安全讀”,而是為了簡化并發程序設計。如果對象不可變:
- 不需要加鎖
- 不需要 volatile
- 不用擔心可見性問題
- 不用寫復雜的同步控制邏輯
只需保證創建時一次寫入,之后多線程隨便讀即可。
不變性模式不是為了實現線程安全,而是為了“讓線程安全成為默認”,并從語言/設計層面強制杜絕修改,從而簡化并發程序的設計。
1.2. 實現不變性模式
1. Java 實現方式
public final class Person {private final String name;private final int age;public Person(String name, int age) {this.name = name;this.age = age;}public Person withAge(int newAge) {return new Person(this.name, newAge);}public String getName() { return name; }public int getAge() { return age; }
}
2. Python 實現方式
from dataclasses import dataclass@dataclass(frozen=True)
class Person:name: strage: intp1 = Person("Alice", 30)
p2 = Person(p1.name, 31) # 通過創建新對象實現“修改”
3. Go 實現方式(沒有語言級別支持,只能靠規范)
type Person struct {name stringage int
}// 提供只讀 getter
func (p Person) Name() string {return p.name
}// 修改返回新對象
func (p Person) WithAge(newAge int) Person {return Person{name: p.name, age: newAge}
}
Immutability 模式注意事項
1. final 屬性 ≠ 不可變
如果類的屬性是對象,即使屬性用 final
修飾,屬性本身不變,屬性引用的對象內容仍然可能被更改。
正確方式:確保引用對象也不可變。
class Foo {int age;
}final class Bar {final Foo foo; // Foo 是可變的
}
2. 不可變對象也需要正確發布
- 不可變對象雖然線程安全,但持有不可變對象的引用不一定是線程安全的。
- 若引用在多個線程間共享,需使用
volatile
或原子類保證可見性/原子性。
原子類示例:
AtomicReference<WMRange> rf = new AtomicReference<>(new WMRange(0, 0));
1.3. 享元模式(Flyweight Pattern)
概念
- 定義:通過復用相同對象,避免重復創建,降低內存消耗。
- 本質:對象池(緩存)。
- 關鍵點:
-
- 對象不可變(否則復用會有副作用)
- 對象的創建要通過工廠/緩存控制
1. Java 中的應用
Java 的 Long.valueOf()
使用享元模式緩存了 [-128,127]
范圍的數值:
static final Long cache[] = new Long[256];static {for (int i = 0; i < cache.length; i++) {cache[i] = new Long(i - 128);}
}Long.valueOf(long l) {if (l >= -128 && l <= 127)return LongCache.cache[(int) l + 128];return new Long(l);
}
鎖問題示例:
Long al = Long.valueOf(1);
Long bl = Long.valueOf(1);
// al == bl,鎖是同一把 → 不安全!
2. Python 中的享元模式
Python 的 int
和 str
對象也使用了享元機制。
a = 100
b = 100
print(a is b) # True,因為緩存了 [-5, 256] 的整數
自定義享元:
class FlyweightFactory:_cache = {}@staticmethoddef get(value):if value not in FlyweightFactory._cache:FlyweightFactory._cache[value] = HeavyObject(value)return FlyweightFactory._cache[value]
3. Go 中的享元模式
Go 中沒有自動享元機制,但可手動實現對象池:
var pool = make(map[string]*User)func GetUser(name string) *User {if u, ok := pool[name]; ok {return u}u := &User{name: name}pool[name] = ureturn u
}
- Go 標準庫中也提供了對象池工具:
sync.Pool
(并非享元,但類似概念)