/**
* 說明: 其中一個任務產生偶數,而其他任務消費這些數字。
* 這裏,消費者任務的唯一工作就是檢查偶數的有效性。
*
* @create @author Henry @date 2016-11-24
*
*/
/**
* 首先,我們定義EvenChecker,即消費者任務,因爲它將在隨後所有的
* 示例中被複用。爲了將EvenChecker與我們要試驗的各種類型的生成器
* 解耦,我們將創建一個名爲IntGenerator的抽象類,它包含EvenChecker
* 必須瞭解的必不可少的方法:即一個Next()方法,和一個可以執行撤銷的方法。
* 這個類沒有實現Generator接口,因爲它必須產生一個int,而泛型不支持基本類型的參數
*
* @create @author Henry @date 2016-11-24
*
*/
public abstract class IntGenerator {
private volatile boolean canceled = false;
public abstract int next();
public void cancel() {
canceled = true;
}
public boolean isCanceled() {
return canceled;
}
}
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 在本例中可以被撤銷的類不是Runnable,而所有依賴於IntGenerator對象的EvenChecker任務
* 將測試它,以查看它是否已經被撤銷,正如你在run()中所見。通過這種方式,共享公共資源(IntGenerator)
* 的任務可以觀察該資源的終止信號。這可以消除所謂競爭條件,即兩個或更多的任務競爭響應某個條件,
* 因此產生衝突或不一致結果的情況。
* 你必須仔細考慮並防範併發系統失敗的所有可能途徑,
* 例如,一個任務不能依賴於另一個任務,因爲任務關閉的順序無法得到保證。
* 這裏通過使任務依賴於非任務對象,我們可以消除潛在的競爭條件。
*
* @create @author Henry @date 2016-11-24
*
*/
public class EvenChecker implements Runnable{
private IntGenerator generator;
private final int id;
public EvenChecker(IntGenerator generator, int id) {
this.generator = generator;
this.id = id;
}
/**
* EvenChecker任務總是讀取和測試從與其相關的IntGenerator返回的值。注意,
* 如果generator.isCanceled()爲true,則run()將返回,這將告知EvenChecker.test()
* 中的Executor該任務完成了。任何EvenChecker任務都可以在與其相關聯的IntGenerator
* 上調用cancel(),這將導致所有其他使用該IntGenerator的EvenChecker得體地關閉。
* 在後面個節中,你將看到Java包含的用於線程終止的各種更通用的機制。
*
* @create @author Henry @date 2016-11-24
*/
@Override
public void run() {
while(!generator.isCanceled()){
int val=generator.next();
if(val%2!=0){
System.out.println(val+" not even!");
generator.cancel();//Cancels all EvenCheckers
}
}
}
/**
* test() 方法通過啓動大量使用相同的IntGenerator的EvenChecker,設置並執行對任何類型的
* IntGenerator的測試。如果IntGenerator引發失敗,那麼test()將報告它並返回,
* 否則,你必須按下Control-C來終止它。
*
* @create @author Henry @date 2016-11-24
* @param gp
* @param count
*/
public static void test(IntGenerator gp,int count){
System.out.println("Press Control-C to exit");
ExecutorService exec=Executors.newCachedThreadPool();
for (int i = 0; i < count; i++)
exec.execute(new EvenChecker(gp, i));
exec.shutdown();
}
public static void test(IntGenerator gp){
test(gp, 10);
}
}
/**
* 一個任務有可能在另一個任務執行第一個對currentEvenValue的遞增操作之後,
* 但是沒有執行第二個操作之前,調用next()方法(即,代碼中被註釋爲
* "Danger point here!"的地方)。這將使這個值處於"不恰當"的狀態。爲了證明這是可能發生的,
* EvenChecker.test()創建了一組EvenChecker對象,以連續地讀取並輸出同一個
* EvenGenerator,並測試檢查每個數值是否都是偶數。如果不是,就會報告錯誤,而程序也將關閉。
*
*
* @create @author Henry @date 2016-11-24
*
*/
public class EventGenerator extends IntGenerator{
private int currentEvenValue=0;
/**
* 如果你正在寫一個變量,它可能接下來將被另一個線程讀取,
* 或者正在讀取一個上次已經被另一個線程寫過的變量,那麼你必須使用同步,並且,
* 讀取線程都必須使用相同的監視器鎖同步。
*
* @create @author Henry @date 2016-11-24
*/
@Override
public int next() {//synchronized
++currentEvenValue;//Danger point here!
//Thread.yield();
++currentEvenValue;
return currentEvenValue;
}
/**
* 這個程序最終將失敗,因爲各個EvenChecker任務在EvenGenerator處於"不恰當的"狀態時,
* 仍能夠訪問其中的信息。但是,更加你使用的特定操作系統和其他實現細節,直到
* EvenCenerator完成多次循環之前,這個問題都不會被探測到。如果你希望更快地發現
* 失敗,可以嘗試着將對yield()的調用放置在第一個和第二個遞增操作之間。這只是併發程序的部分問題
* 如果失敗的概率非常低,那麼即使存在缺陷,它們也可能看起來是正確的 。
*
* 有一點很重要,那就是要注意到遞增程序自身也需要多個步驟,並且在遞增過程中任務
* 可能會被線程機制掛起---也就是說,在Java中,遞增不是原子性的操作。因爲,如果
* 不保護任務,即使單一的遞增也是不安全的。
*
* @create @author Henry @date 2016-11-24
* @param args
*/
public static void main(String[] args) {
EvenChecker.test(new EventGenerator());
}
}
用lock解決問題
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* MutexEvenGenerator添加了一個被互斥調用的鎖,並使用lock()和unlock()方法
* 在next()內部創建了臨界資源。當你在使用Lock對象時,將這裏所示的慣用法內部化是很重要的:
* 緊接着對lock()的調用,你必須放置在finally子句中帶有unlock()的try-finally語句中。
* 注意,return語句必須在try子句中出現,以確保unlock()不會過早發生,從而將數據暴露給了
* 第二個任務。
*
* @create @author Henry @date 2016-11-24
*
*/
public class MutexEvenGenerator extends IntGenerator {
private int currentEvenValue = 0;
private Lock lock=new ReentrantLock();
/**
* 儘管try-finally所需的代碼比synchronized關鍵字要多,但是
* 這也代表了顯示的Lock對象的優點之一。如果使用synchronized關鍵字時,
* 某些事物失敗了,那麼就會拋出一個異常。但是你沒有機會去做任何清理工作,
* 以維護系統使其處於良好狀態。有了顯式的Lock對象,你就可以使用finally
* 子句將系統維護在正確的狀態了。
*
* 大體上,當你使用sunchronized關鍵字時,需要寫的代碼量更少,並且用戶錯誤出現
* 的可能性也會降低,因爲通常只有在解決特殊問題時,才使用顯式的Lock對象。
* 例如,用synchronized關鍵字不能嘗試着獲取鎖且最終獲取鎖會失敗,或者嘗試着
* 獲取鎖一段時間,然後放棄它,要實現這些,你必須使用concurrent類庫。
*
*
* @create @author Henry @date 2016-11-24
*/
@Override
public int next() {
lock.lock();
try{
++currentEvenValue;//Danger point here!
Thread.yield();
++currentEvenValue;
return currentEvenValue;
}finally{
lock.unlock();
}
}
public static void main(String[] args) {
EvenChecker.test(new MutexEvenGenerator());
}
}
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
/**
* ReentrantLock 允許你嘗試着獲取單最終未獲取鎖,這樣如果其他人已經獲取了這個鎖,那你
* 就可以決定離開去執行其他一些事情,而不是等待直到這個鎖被釋放,就像在untimed()方法中
* 所看到的。在timed()中,作出了嘗試去嘗試獲取鎖,該嘗試可以在兩秒之後失敗(注意,使用
* 了java se5 的TimeUnit類來指定時間單位)。在main中,作爲匿名類而創建了一個單位的Thread,
* 它將獲取鎖,這使得untimed()和timed()方法對某些事物將產生競爭。
*
* 顯示的lock對象在枷鎖和釋放鎖方面,相對於內建的synchronized鎖來說,還賦予了你更細粒度的
* 控制力。這對於實現專有同步結構是很有用的,例如用於遍歷鏈接列表中的節點,節節傳遞的枷鎖機制
* (也稱爲鎖耦合),這種遍歷代碼必須在釋放當前節點的鎖之前不活下一個節點鎖。
*
* @create @author Henry @date 2016-11-28
*
*/
public class AttemptLocking {
private ReentrantLock lock = new ReentrantLock();
public void untimed() {
boolean captured = lock.tryLock();
try {
System.out.println("tryLock():" + captured);
} finally {
if (captured)
lock.unlock();
System.out.println("untimed over");
}
}
public void timed() {
boolean captured = false;
try {
captured = lock.tryLock(2, TimeUnit.SECONDS);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
try {
System.out.println("tryLock(2, TimeUnit.SECONDS):" + captured);
} finally {
if (captured)
lock.unlock();
System.out.println("timed over");
}
}
public static void main(String[] args) {
final AttemptLocking al = new AttemptLocking();
al.untimed();// True --lock is available
al.timed();// True -- lock is available
// Now create a separate task to grab the lock;
new Thread() {
{
//setDaemon(true);
}
public void run(){
al.lock.lock();
System.out.println("acquired");
}
}.start();
Thread.yield(); //Give the 2nd tesk a chance
al.untimed(); //False --lock grabbed by task
al.timed(); //False --Lock grabbed by task
}
}