import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/**
* 裝飾性花園
*
* 在這個仿真程序中,花園委員會希望瞭解每天通過多個大門進入公共的總人數。
* 每個大門都有一個十字轉門或某種其他形式的計數器,並且任何一個十字轉門
* 的計數值遞增時,就表示公園中的總人數的共享計數值也會遞增。
*
* @create @author Henry @date 2016-12-06
*
*/
/**
* 這裏使用單個的Count對象來跟蹤還原參觀者的主計數值,並且將其當作Entrance類中的一個
* 靜態域進行存儲。Count.increment()和Count.value()都是synchronized的,
* 用來控制對count域的訪問。
*
* @create @author Henry @date 2016-12-06
*/
class Count {
private int count = 0;
private Random rand = new Random(47);
/**
* increment()方法使用了Random對象,
* 目的是從把count讀取到temp中,到遞增temp並將其存儲回count的這段時間裏,
* 有大約一半的時間產生讓步。
* 如果你將increment()上的synchronized關鍵字註釋掉,那麼這個程序就會崩潰,
* 因爲多個任務將同時訪問並修改count(yield()會使問題更快地發生)
* @create @author Henry @date 2016-12-06
*
* @return
*/
// Remove the synchronized keyword to see counting fail:
public synchronized int increment() {
int temp = count;
if (rand.nextBoolean())// Yield half the time
Thread.yield();
return (count = ++temp);
}
public synchronized int value() {
return count;
}
}
/**
* Entrance類中的有一個Count對象做爲靜態域進行存儲。
* 每個Entrance任務都維護着一個本地值number,它包含通過某個特定入口進入的參觀者的
* 數量。這提供了對count對象的雙重檢查,以確保其記錄的參觀者數量是正確的。Entrance.run()
* 只是遞增number和count對象,然後休眠100毫秒。
* 因爲Entrance.canceled是一個volatile布爾標誌,而它只會被讀取和賦值
* (不會與其他域組合在一起唄讀取),所以不需要同步對其的訪問,就可以安全地操作它。
* 如果你對諸如此類的情況有任何疑慮,那麼最好總是使用synchronized。
*
* @create @author Henry @date 2016-12-06
*
*/
class Entrance implements Runnable {
private static Count count = new Count();
private static List<Entrance> entrances = new ArrayList<Entrance>();
private int number = 0;
// Doesn't need synchronization to read:
private final int id;
private static volatile boolean canceled = false;
// Atomic operation on a volatile field:
public static void cancel() {
canceled = true;
}
public Entrance(int id) {
this.id = id;
// Keep this task in a list. Also prevents
// garbage collection of dead tasks:
entrances.add(this);
}
@Override
public void run() {
while (!canceled) {
synchronized (this) {
++number;
}
System.out.println(this + " Total: " + count.increment());
try {
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException e) {
System.out.println("sleep interrupted");
}
}
System.out.println("Stopping " + this);
}
public synchronized int getValue() {
return number;
}
@Override
public String toString() {
return "Entrance " + id + ": " + getValue();
}
public static int getTotalCount(){
return count.value();
}
public static int sumEntrances(){
int sum=0;
for (Entrance entrance : entrances)
sum+=entrance.getValue();
return sum;
}
}
/**
* 這個程序在以穩定的方式關閉所有事物方面還有一些小麻煩,其部分原因是爲了說明在終止
* 多線程程序時你必須相當小心,而另一部分原因是爲了演示interrupt()值,稍後你將學習
* 有關這個值的知識。
*
* 在3秒鐘之後,main()向Entrance發送static cancel()消息,然後調用exec對象的
* shutdown()方法,之後調用exec上的awaitTermination()方法。
* ExecutorService.awaitTermination()等待每個任務結束,如果所有的任務在超時時間到達
* 之前全部結束,則返回true,否則返回false,表示不是所有的任務都已經結束了。儘管這會導致
* 每個任務都退出其run()方法,並因此作爲任務而終止,但是Entrance對象仍舊是有效的,
* 因爲在構造器中,每個Entrance對象都存儲在稱爲entrances的靜態List<Entrance>中。
* 因此,sumEntrances()仍舊可以作用月這些有效的Entrance對象。
*
* 當這個程序運行時,你將看到,在人們通過十字轉門時,將顯示總人數和通過每個入口的人數。
* 如果移除Count.increment()上面的synchronized聲明,你將會注意到總人數與你的期望有差異,
* 每個十字轉門統計的人數將與count中的值不同。只要用互斥來同步對Count的訪問,問題就可以解決了
* 請記住,Count.increment()通過使用temp和yield(),增加了失敗的可能性。在真正的
* 線程問題中,失敗的可能性從統計學角度可能非常小,因此你可能很容易就掉進了輕信所有事物都將正確
* 工作的陷阱裏。就像在上面的示例中,有些還未發生問題就有可能會隱藏起來,因此在複審併發代碼時,
* 要格外地仔細。
*
*
*
* @create @author Henry @date 2016-12-06
*/
public class OrnamentalGarden {
public static void main(String[] args) throws Exception {
ExecutorService exec=Executors.newCachedThreadPool();
for (int i = 0; i < 5; i++)
exec.execute(new Entrance(i));
TimeUnit.SECONDS.sleep(3);
Entrance.cancel();
exec.shutdown();
if(!exec.awaitTermination(250, TimeUnit.MILLISECONDS))
System.out.println("Some tasks were not teminated");
System.out.println("Total: "+Entrance.getTotalCount());
System.out.println("Sum of Entrances:"+Entrance.sumEntrances());
}
}
java 多線程 終結任務 裝飾性花園
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.