Java 不可变对象
如果一个对象的状态在构造后不能改变,则该对象被认为是不可变的。例如 String。
不可变对象在并发应用程序中特别有用。由于它们不能改变状态,因此它们不会被线程干扰破坏或在不一致状态下被观察到。
定义一个普通同步类(可变对象)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package com.example.Immutable; public class SynchronizedRGB { // 值必须介于0到255之间 private int red; private int green; private int blue; private String name; private void check(int red, int green, int blue) { if (red < 0 || red > 255 || green < 0 || green > 255 || blue < 0 || blue > 255) { throw new IllegalArgumentException(); } } public SynchronizedRGB(int red, int green, int blue, String name) { check(red, green, blue); this.red = red; this.green = green; this.blue = blue; this.name = name; } public void set(int red, int green, int blue, String name) { check(red, green, blue); synchronized (this) { this.red = red; this.green = green; this.blue = blue; this.name = name; } } public synchronized int getRGB() { return ((red << 16) | (green << 8) | blue); } public synchronized String getName() { return name; } public synchronized void invert() { red = 255 - red; green = 255 - green; blue = 255 - blue; name = "Inverse of " + name; } }
假设一个线程执行以下代码:
SynchronizedRGB color = new SynchronizedRGB(0, 0, 0, "Pitch Black"); ... int myColorInt = color.getRGB(); //Statement 1 String myColorName = color.getName(); //Statement 2
如果另一个线程 color.set() 在语句1之后但在语句2之前调用,则 myColorInt 将与 myColorName 不匹配 。为了避免这种结果,必须将两个语句绑定在一起:
synchronized(color){ int myColorInt = color.getRGB(); String myColorName = color.getName(); }
这种不一致性只适用于可变对象。
定义一个同步类(不可变对象)
以下规则定义了用于创建不可变对象的简单策略。但并非所有“不可变”的类都遵循这些规则:
1.不要提供属性的 setter 方法
2.所有字段用 final 和 private 修饰
3.不允许子类重写方法。最简单的方法是将类声明为 final。更复杂的方法是 private 在工厂方法中构造构造函数和构造实例
4.如果实例字段包含对可变对象的引用,则不允许更改这些对象
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package com.example.Immutable; final public class ImmutableRGB { // 值必须介于0到255之间 final private int red; final private int green; final private int blue; final private String name; private void check(int red, int green, int blue) { if (red < 0 || red > 255 || green < 0 || green > 255 || blue < 0 || blue > 255) { throw new IllegalArgumentException(); } } public ImmutableRGB(int red, int green, int blue, String name) { check(red, green, blue); this.red = red; this.green = green; this.blue = blue; this.name = name; } public int getRGB() { return ((red << 16) | (green << 8) | blue); } public String getName() { return name; } public ImmutableRGB invert() { return new ImmutableRGB(255 - red, 255 - green, 255 - blue, "Inverse of " + name); } }
只有一个线程引用一个对象时,则该对象本身就是不可变的。因此,不需要防止改变的保护措施。
https://docs.oracle.com/javase/tutorial/essential/concurrency/immutable.html