java并发编程实战-第4章-对象的组合

4.3.5 重点:理解发布状态可变的车辆追踪器

    理解脚注:

    p58页:如果将拷贝构造函数实现为this(p.x,p.y),那么会产生竞态 条件,而私有构造函数则可以避免这种竞态条件.这是私有构造函数捕获模式的一个实例。

public class Main {

	public static void main(String[] args) {
		final SafePoint originalSafePoint = new SafePoint(1, 1);

		// One Thread is trying to change this SafePoint
		new Thread(new Runnable() {
			@Override
			public void run() {
				originalSafePoint.set(2, 2);
				System.out.println("Original : " + originalSafePoint.toString());
			}
		}).start();

		// The other Thread is trying to create a copy. The copy, depending on
		// the JVM, MUST be either (1,1) or (2,2)
		// depending on which Thread starts first, but it can not be (1,2) or
		// (2,1) for example.
		new Thread(new Runnable() {
			@Override
			public void run() {
				SafePoint copySafePoint = new SafePoint(originalSafePoint);
				System.out.println("Copy : " + copySafePoint.toString());
			}
		}).start();
	}

}

// 线程安全类
class SafePoint {

	private int x, y;

	private SafePoint(int[] a) {
		this(a[0], a[1]);
	}

	public SafePoint(SafePoint p) {
		this(p.get());
	}

	public SafePoint(int x, int y) {
		this.x = x;
		this.y = y;
	}

	public synchronized int[] get() {
		return new int[] { x, y };
	}

	public synchronized void set(int x, int y) {
		this.x = x;
		// Simulate some resource intensive work that starts EXACTLY at this
		// point, causing a small delay
		try {
			Thread.sleep(10 * 100);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		this.y = y;
	}

	@Override
	public String toString() {
		return "(" + x + "," + y + ")";
	}
}

// 线程不安全类
class SafePoint2 {

	private int x;
	private int y;

	public SafePoint2(int x, int y) {
		this.x = x;
		this.y = y;
	}

	public SafePoint2(SafePoint2 safePoint2) {
		this(safePoint2.x, safePoint2.y);
	}

	public synchronized int[] get() {
		return new int[] { x, y };
	}

	public synchronized void set(int x, int y) {
		this.x = x;
		// Simulate some resource intensive work that starts EXACTLY at this
		// point, causing a small delay
		try {
			Thread.sleep(10 * 100);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		this.y = y;
	}

	@Override
	public String toString() {
		return "(" + x + "," + y + ")";
	}

当main中使用线程安全类SafePoint时,运行结果为:

Copy : (2,2)

Original : (2,2)

当main中使用线程不安全类SafePoint2时,运行结果为:

Copy : (2,1)

Original : (2,2)

原因:SafePoint2为线程不安全类,是因为发生了竞态条件,本质上是因为写操作set(int x, int y)和拷贝构造函数中的读操作this(safePoint2.x, safePoint2.y)不同步,发生竞态条件造成的。核心原因是类的构造函数不能使用同步机制

而get( )是加锁同步的,是可以安全使用的,因此可以构造一个接受get( )参数的私有构造函数(设为私有,防止外部调用):

private SafePoint(int[] a) {
		this(a[0], a[1]);
	}

再构造一个使用上述构造函数的公有拷贝构造函数

public SafePoint(SafePoint p) {
		this(p.get());
	}

这样公有拷贝构造函数不会与写操作set(int x, int y)发生竞态条件,可以安全的拷贝旧对象或最新对象,保证x和y处于一致(处于同一不变性条件),但个人觉得SafePoint类仍然存在线程安全问题(没有将x, y设为final),参照Java并发编程实战P42,3.5.1节。

    具体参考stackoverflow上Eugene的精彩的解答:http://stackoverflow.com/questions/12028925/private-constructor-to-avoid-race-condition/12029346


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值