Written by
Yitong Huang
on
on
Java高并发 - 18.不可变对象设计模式
无论是synchronized关键字还是显式锁Lock,都会牺牲系统的性能。可以在线程之间不共享资源状态,甚至将资源的状态设置为不可变。不可变对象的设计理念已经越来越流行,其中Actor模型以及函数式编程语言Clojure等都依赖于不可变对象的设计达到lock free。
Java核心类库中提供大量的不可变对象范例,例如java.lang.String的每一个方法都没有同步修饰,但是它在多线程访问的情况下是安全的。Java8中通过Stream修饰的ArrayList在函数式方法并行访问的情况下也是线程安全的。所谓的不可变对象是没有机会去修改它,每一次修改都会导致一个新的对象产生。
非线程安全可变对象被不可变机制加以处理之后,同样具备不可变性。例如,ArrayList生成的stream在多线程的情况下也是线程安全的。
public class ArrayListStream {
public static void main(String[] args) {
// 定义一个list并且使用Arrays的方式进行初始化
List<String> list = Arrays.asList("Java", "Thread", "Concurrency", "Scala", "Clojure");
//获取并行的stream,然后通过map函数对list中的数据进行加工,最后输出
list.parallelStream().map(String::toUpperCase).forEach(System::out::println);
list.forEach(System::out::println);
}
}
list虽然是在并行环境下运行的,但是在stream的每一个操作中都是一个新的List,根本不会影响到最原始的list。
下面实现一个累加器,包括非线程安全、方法同步增加线程安全性和不可变三种实现方式。
非线程安全的累加器
public class IntegerAccumulator {
private int init;
// 构造时传入初始值
public IntegerAccumulator(int init) {
this.init = init;
}
// 对初始值增加i
public int add(int i) {
this.init += i;
return this.init;
}
// 返回当前的初始值
public int getValue() {
return this.init;
}
private static void slowly() {
try {
TimeUnit.MILLISECONDS.sleep(1);
} catch(InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
// 定义累加器,并且将初始值设置为0
IntegerAccumulator accumulator = new IntegerAccumulator(0);
// 定义三个线程,并且分别启动
IntStream.range(0, 3).forEach(i -> new Thread(() -> {
int inc = 0;
while(true) {
// 首先获得old value
int oldValue = accumulator.getValue();
// 然后调用add方法计算
int result = accumulator.add(inc);
System.out.println(oldValue + "+" + inc + "=" + result);
// 经过验证,如果不合理,则输出错误信息
if (inc + oldValue != result) {
System.err.println("ERROR:" + oldValue + "+" + inc + "=" + result);
}
inc++;
// 模拟延迟
slowly();
}
}).start());
}
}
方法同步增加线程安全性
public static void main(String[] args) {
IntegerAccumulator accumulator = new IntergerAccumulator(0);
InitStream.range(0, 3).forEach(i -> new Thread(() -> {
int inc = 0;
while(true) {
int oldValue;
int result;
// 使用class示例作为同步锁
synchronized(IntegerAccumulator.class) {
oldValue = accumulator.getValue();
result = accumulator.add(inc);
}
System.out.println(oldValue + "+" + inc + "=" + result);
if (inc + oldValue != result) {
System.err.println("ERROR:" + oldValue + "+" + inc + "=" + result);
}
inc++;
slowly();
}
}).start());
}
不可变的累加器对象设计
// 不可变对象不允许被继承
public final class IntegerAccumulator {
private final int init;
// 构造时传入初始值
public IntegerAccumulator(int init) {
this.init = init;
}
// 构造新的累加器,需要用到另一个accumulator和初始值
public IntegerAccumulator(IntegerAccumulator accumulator, int init) {
this.init = accumulator.getValue() + init;
}
// 每次相加都会产生一个新的IntegerAccumulator
public IntegerAccumulator add(int i) {
return new IntegerAccumulator(this, i);
}
public int getValue() {
return this.init;
}
private static void slowly() {
try {
TimeUnit.MILLISECONDS.sleep(1);
} catch(InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
// 用同样的方式进行测试
IntegerAccumulator accumulator = new IntegerAccumulator(0); IntStream.range(0, 3).forEach(i -> new Thread(() -> {
int inc = 0;
while(true) {
int oldValue = accumulator.getValue(); int result = accumulator.add(inc);
System.out.println(oldValue + "+" + inc + "=" + result); if (inc + oldValue != result) {
System.err.println("ERROR:" + oldValue + "+" + inc + "=" + result);
}
inc++;
slowly();
}
}).start());
}
}