Java并发编程实战~Immutability模式

58 篇文章 5 订阅

        解决并发问题,其实最简单的办法就是让共享变量只有读操作,而没有写操作。这个办法如此重要,以至于被上升到了一种解决并发问题的设计模式:不变性(Immutability)模式。所谓不变性,简单来讲,就是对象一旦被创建之后,状态就不再发生变化。换句话说,就是变量一旦被赋值,就不允许修改了(没有写操作);没有修改操作,也就是保持了不变性。

快速实现具备不可变性的类

        将一个类所有的属性都设置成 final 的,并且只允许存在只读方法,那么这个类基本上就具备不可变性了。更严格的做法是这个类本身也是 final 的,也就是不允许继承。因为子类可以覆盖父类的方法,有可能改变不可变性,所以推荐你在实际工作中,使用这种更严格的做法。

        经常用到的 String 和 Long、Integer、Double 等基础类型的包装类都具备不可变性。

利用享元模式避免创建重复对象

        享元模式本质上其实就是一个对象池,利用享元模式创建对象的逻辑也很简单:创建之前,首先去对象池里看看是不是存在;如果已经存在,就利用对象池里的对象;如果不存在,就会新创建一个对象,并且把这个新创建出来的对象放进对象池里。

使用 Immutability 模式的注意事项

1. 对象的所有属性都是 final 的,并不能保证不可变性;
2. 不可变对象也需要正确发布。

例如下面的代码中,Bar 的属性 foo虽然是 final 的,依然可以通过 setAge() 方法来设置 foo 的属性 age。所以,在使用Immutability 模式的时候一定要确认保持不变性的边界在哪里,是否要求属性对象也具备不可变性。

class Foo{
	int age = 0;
	int name = "abc";
}

final class Bar {
	final Foo foo;
	
	void setAge(int a){
		foo.age = a;
	}
}

下面我们再看看如何正确地发布不可变对象。不可变对象虽然是线程安全的,但是并不意味着引用这些不可变对象的对象就是线程安全的。例如在下面的代码中,Foo 具备不可变性,线程安全,但是类 Bar 并不是线程安全的,类 Bar 中持有对 Foo 的引用 foo,对 foo这个引用的修改在多线程中并不能保证可见性和原子性。

//Foo 线程安全
final class Foo{
	final int age = 0;
	final int name = "abc";
}

//Bar 线程不安全
class Bar {
	Foo foo;
	void setFoo(Foo f){
		this.foo = f;
	}
}

如果你的程序仅仅需要 foo 保持可见性,无需保证原子性,那么可以将 foo 声明为volatile 变量,这样就能保证可见性。如果你的程序需要保证原子性,那么可以通过原子类来实现。

public class SafeWM {
	class WMRange{
		final int upper;
		final int lower;
		
		WMRange(int upper,int lower){
			// 省略构造函数实现
		}
	}
	
	final AtomicReference<WMRange>rf = new AtomicReference<>(new WMRange(0,0));
	
	// 设置库存上限
	public void setUpper(int v){
		while(true){
			WMRange or = rf.get();
			// 检查参数合法性
			if(v < or.lower){
				throw new IllegalArgumentException();
			}
			WMRange nr = new WMRange(v, or.lower);
			if(rf.compareAndSet(or, nr)){
				return;
			}
		}
  }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值