定义:对象一旦被创建,其内部状态就不再发生变化,也即“只读无写”,不会出现并发写的问题,自然线程安全。
适用场景:只读共享数据场景,是并发编程中最简单直接的线程安全保障方案。
不变性模式的作用:
不变性模式的实际作用不是为了“能不能安全读”,而是为了简化并发程序设计。如果对象不可变:
只需保证创建时一次写入,之后多线程随便读即可。
不变性模式不是为了实现线程安全,而是为了“让线程安全成为默认”,并从语言/设计层面强制杜绝修改,从而简化并发程序的设计。
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: str
age: int
p1 = Person("Alice", 30)
p2 = Person(p1.name, 31) # 通过创建新对象实现“修改”
3. Go 实现方式(没有语言级别支持,只能靠规范)
type Person struct {
name string
age 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 rf = new AtomicReference<>(new WMRange(0, 0));
概念
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 = {}
@staticmethod
def 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] = u
return u
}
sync.Pool
(并非享元,但类似概念)