java 多線程 臨界區的操作

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Not thread-safe
 * 正如註釋中註明的,Pair不是線程安全的,因爲它的約束條件(雖然是任意的)需要兩個變量要維護成相同的值。
 * 此外,如本章前面所描述的,自增加操作不是線程安全的,並且因爲沒喲任何方法被標記爲synchronized,所以
 * 不能保證一個Pair對象在多線程程序中不會被破壞。
 * 
 * @create @author Henry @date 2016-11-30
 */
class Pair {
	private int x, y;

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

	public Pair() {
		this(0, 0);
	}

	public int getX() {
		return x;
	}

	public int getY() {
		return y;
	}

	public void incrementX() {
		x++;
	}

	public void incrementY() {
		y++;
	}

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

	public class PairValuesNotEqualExceptin extends RuntimeException {
		public PairValuesNotEqualExceptin() {
			super("Pair values not equal : " + Pair.this);
		}
	}

	/**
	 * Arbitrary invariant --both variables must be equal:
	 * 
	 * @create @author Henry @date 2016-11-30
	 */
	public void checkState() {
		if (x != y)
			throw new PairValuesNotEqualExceptin();
	}
}
/**
 * 你可以想象一下這種情況:某人交個你一個非線程安全的Pair類,而你需要在一個線程環境中使用它。通過創建
 * PairManager類就可一個實現這一點,PairManager類持有一個Pair對象並控制它的一切訪問。注意唯一的
 * public 方法是 getPair(),它是synchronized的。對於抽象方法increment(),對於increment()的同步
 * 控制將在實現的時候進行處理。
 */
/**
 * Protect a Pair inside a thread-safe class:
 * 
 * 至於PairManager類的結構,它的一些功能在積累中實現,並且其一個或多個抽象方法在派生類中定義,
 * 這種結構在設計模式中成爲模板方法。設計模式使你得以把變化封裝在代碼裏;因此,發生變化的部分是
 * 模板方法increment()。
 * store()方法將一個Pair對象添加到了Synchronized ArrayList中,所以這個操作是線程安全的。
 * 因此,該方法不必進行防護,可以防止在PairManager2的synchronized語句塊的外部。
 * 
 * @create @author Henry @date 2016-11-30
 */
abstract class PairManager {
	AtomicInteger checkCounter = new AtomicInteger(0);
	protected Pair p = new Pair();
	private List<Pair> storage = Collections.synchronizedList(new ArrayList<Pair>());

	/**
	 * Make a copy to keep the original safe
	 * 
	 * @create @author Henry @date 2016-11-30
	 * @return
	 */
	public synchronized Pair getPair() {
		return new Pair(p.getX(), p.getY());
	}

	protected void store(Pair p) {
		storage.add(p);
		try {
			TimeUnit.MILLISECONDS.sleep(50);
		} catch (InterruptedException e) {
		}
	}

	public abstract void increment();
}

/**
 * Synchronized the entire method:
 * 
 * 在PairManager1中,整個increment()方法被同步控制。
 * 
 * @create @author Henry @date 2016-11-30
 * 
 */
class PairManager1 extends PairManager {

	@Override
	public synchronized void increment() {
		p.incrementX();
		p.incrementY();
		store(getPair());
	}
}

/**
 * Use a critical section:
 * 
 * 在PairManager2中,increment()方法使用同步控制塊進行同步。
 * 注意,synchronized關鍵字不屬於方法特性簽名的組成部分,所以可以在覆蓋方法的時候加上去。
 * 
 * @create @author Henry @date 2016-11-30
 * 
 */
class PairManager2 extends PairManager {
	@Override
	public void increment() {
		Pair temp;
		synchronized (this) {
			p.incrementX();
			p.incrementY();
			temp = getPair();
		}
		store(temp);
	}
}
/**
 * PairManipulator被創建用來測試兩種不同類型的PairManager,其方法是在某個任務中調用increment(),
 * 
 * @create @author Henry @date 2016-11-30
 *
 */
class PairManipulator implements Runnable {
	private PairManager pm;

	public PairManipulator(PairManager pm) {
		this.pm = pm;
	}

	@Override
	public void run() {
		while (true)
			pm.increment();
	}

	@Override
	public String toString() {
		return "Pair: " + pm.getPair() + " checkCounter = " + pm.checkCounter.get();
	}
}
/**
 * PairChecker則在另一個任務中執行。爲了跟蹤可以運行測試的頻度,
 * PairChecker在每次成功時都遞增checkCounter。
 * 
 * @create @author Henry @date 2016-11-30
 *
 */
class PairChecker implements Runnable {
	private PairManager pm;

	public PairChecker(PairManager pm) {
		this.pm = pm;
	}

	@Override
	public void run() {
		while (true) {
			pm.checkCounter.incrementAndGet();
			pm.getPair().checkState();
		}
	}
}

/**
 * Test the two different approaches;
 * 在main()中創建了兩個PairManipulator對象,並運行它們運行一段時間,
 * 之後每個PairManipulator的結果會得到展示。
 * 
 * @create @author Henry @date 2016-11-30
 * 
 */
public class CriticalSection {
	static void testApproaches(PairManager pman1, PairManager pman2) {
		ExecutorService exec = Executors.newCachedThreadPool();
		PairManipulator pm1 = new PairManipulator(pman1), pm2 = new PairManipulator(pman2);
		PairChecker pcheck1 = new PairChecker(pman1), pcheck2 = new PairChecker(pman2);
		exec.execute(pm1);
		exec.execute(pm2);
		exec.execute(pcheck1);
		exec.execute(pcheck2);
		try {
			TimeUnit.MILLISECONDS.sleep(1000);
		} catch (InterruptedException e) {
			System.out.println("Sleep interrupted");
		}
		System.out.println("pm1: " + pm1 + "\npm2: " + pm2);
		System.exit(0);
	}
	/**
	 * 儘管每次運行的結果可能會非常不同,但一般來說,對於PairChecker的檢查頻率。
	 * PairManager1.increment()不允許有PairManager2.increment()那樣多。
	 * 後者採用溝通不控制塊進行同步,所以對象不加鎖的時間更長。這也是寧願使用同步控制塊
	 * 而不是對整個方法進行同步控制的典型原因:使得其他線程能更多地訪問
	 * (在安全的情況下儘可能多)。
	 * 
	 * 運行結果:
	 * pm1: Pair: x: 17, y: 17 checkCounter = 1714855
	 * pm2: Pair: x: 22, y: 22 checkCounter = 12120719
	 * @param args
	 */
	public static void main(String[] args) {
		PairManager pman1 = new PairManager1(), pman2 = new PairManager2();
		testApproaches(pman1, pman2);
	}
}
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Synchronize the entire method:
 * 
 * @create @author Henry @date 2016-11-30
 *
 */
class ExplicitPairManager1 extends PairManager {
	private Lock lock = new ReentrantLock();

	@Override
	public synchronized void increment() {
		lock.lock();
		try {
			p.incrementX();
			p.incrementY();
			store(getPair());
		} finally {
			lock.unlock();
		}
	}
}
/**
 * Use a critical section
 * 
 * @create @author Henry @date 2016-11-30
 *
 */
class ExplicitPairManager2 extends PairManager {
	private Lock lock = new ReentrantLock();

	@Override
	public void increment() {
		Pair temp;
		lock.lock();
		try {
			p.incrementX();
			p.incrementY();
			temp = getPair();
		} finally {
			lock.unlock();
		}
		store(temp);
	}
}
/**
 * 此方式在我嘗試的時候,直接出現線程不安全的異常。
 * 但是結果是對的。說明lock鎖的是代碼線程安全,而不會鎖對象同步安全。 
 * 
 * 運行結果:
 * Exception in thread "pool-1-thread-4" com.think.no21.no3.Pair$PairValuesNotEqualExceptin: Pair values not equal : x: 3, y: 2
 * 	at com.think.no21.no3.Pair.checkState(CriticalSection.java:65)
 * 	at com.think.no21.no3.PairChecker.run(CriticalSection.java:192)
 * 	at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
 * 	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
 * 	at java.lang.Thread.run(Thread.java:662)
 * pm1: Pair: x: 21, y: 21 checkCounter = 255040
 * pm2: Pair: x: 22, y: 22 checkCounter = 1884633
 * 
 * @create @author Henry @date 2016-11-30
 *
 */
public class ExplicitCriticalSection {
	public static void main(String[] args) {
		PairManager pman1 = new ExplicitPairManager1(), pman2 = new ExplicitPairManager2();
		CriticalSection.testApproaches(pman1, pman2);
	}
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章