java多线程之线程安全-构造函数捕获模式

在java并发编程实战p58页的脚注的时候没看懂,然后查百度,终于理解了,所以在这里记录一下

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

先上代码

public class Main {
 
	public static void main(String[] args) {
		final SafePoint originalSafePoint = new SafePoint(1, 1);
 
		new Thread(new Runnable() {
			@Override
			public void run() {
				originalSafePoint.set(2, 2);
				System.out.println("Original : " + originalSafePoint.toString());
			}
		}).start();
 
		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 + ")";
	}
}

当时没有看懂,这个竟态条件到底在哪里。现在解释一下吧

不安全

  1. 假设当第一个线程在运行originalSafePoint.set(2, 2);的是发生上下文切换,第二个线程开始执行,如果用的是(// 线程不安全类)SafePoint2
  2. 当调用了this.x = x;的时候又发生上下文切换,运行了originalSafePoint.set(2, 2);,
  3. 然后又发生上下文切换,被修改过的y又被 this.y = y;这样复制了

安全

  1. 假设当第一个线程在运行originalSafePoint.set(2, 2);的是发生上下文切换,第二个线程开始执行,如果用的是(// 线程安全类)SafePoint
  2. 会调用this(p.get());,而这时锁还被第一个运行set的线程持有会阻塞,直道set操作完成,锁才释放
  3. 这样p.get()得到了set后的正确值,就没有线程安全问题啦

你可能感兴趣的:(java多线程)