Immutable就是不可变, 不发生改变的意思, Immutable模式中存在着确保实例状态不发生改变的类(Immutable类), 在访问这些实例时并不需要执行耗时的互斥处理, 因此如果能巧妙利用该模式, 可以提高程序的性能
类名 | 说明 |
Person | 表示人的实体类 |
Main | 测试主程序 |
PrintPersonThread | 显示Person实例的线程类 |
代码中Person类用于表示人, 包含name和address两个字段
Person类中的字段只能通过构造函数赋值, 类中有getter方法, 但是并没有setter方法, 因此Person类实例一旦创建, 其字段的值就不会发生改变
这样即便有多个线程访问同一个实例, Person类也是安全的, Person类中的所有方法也都允许多个线程同时执行, Person类中的getter方法, 和toString()方法, 也无需声明为syncronized
Person类声明为了final类型, 这表示无法创建Person类的子类, 虽然这不是Immutable模式的必要, 但是也能防止子类的继承而修改Person类中的字段的值
Person类的字段name和address都设置为private, 也就是说, 这两个字段都只有从Person类中的内部才能访问, 这也不是Immutable模式的必要条件, 但也是防止子类修改字段值的一种措施, 另外, 这2个字段都声明为了final, 意味着一旦被复制, 就不会被修改
public class PrintPersonThread extends Thread {
private Person person;
public PrintPersonThread(Person person) {
this.person = person;
}
@Override
public void run() {
while (true) {
System.out.println(Thread.currentThread().getName() + "print" + person);
}
}
static class Main {
public static void main(String[] args) {
Person person = new Person("alice", "Alaska");
new PrintPersonThread(person).start();
new PrintPersonThread(person).start();
new PrintPersonThread(person).start();
}
}
}
@Getter
public final class Person {
private final String name;
private final String address;
public Person(String name, String address) {
this.name = name;
this.address = address;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", address='" + address + '\'' +
'}';
}
}
运行结果可以看到, 多个线程再调用toString()方法时, 字段值并没有出现异常
Immutable角色是一个类, 在这个角色中, 字段的值不可被修改, 也不存在修改字段值的方法, Immutable角色的实例被创建后, 状态将不可在被修改, 这样Immutable角色的方法, 就不用再声明为synchronized, Person正是Immutable角色
Immutable模式改在哪些情况下使用呢, 换言之, 开发人员应该在什么情况下考虑不可变呢?
首先, 如前文所述, "实例化创建后, 状态不再发生改变" 是必要条件, 实例的状态是由字段的值决定的, 所以将字段声明为final, 且不提供setter方法, 是重点所在, 但是即使字段的值不可变的, 字段引用的实例也会发生改变, 比如把Person的字段类型从Strin改为Stringbuffer
Immutable模式的有优点是 "不需要syncchronized进行保护", 那就意味着能够在不失去安全性的前提下, 提高性能, 当实例被多个线程共享, 且有可能被频繁访问时, Immutable模式的优点就会凹凸显出来
这里介绍一些java的标椎库中用到的Immutable模式
该类在创建完实例之后, 字符串的内容不会再发生变化
java.math.BigInteget 和 java.math.BigDecimal 类都是Immutable类, BigInteget表示有精度的整数, BigDecimal表示所有精度的数, 这两个类在实例赋值之后就不会再发生变化
java.util.regex.pattern 是Immutable模式, Pattern表示正则表达式的模式, 但即使在处理模式匹配时, 值也不会发生变化
基本类型的包装类, 都是Immutable模式, 在实例化后, 实例包装的值不会再发生变化