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());
	}
}