利用能量守恒定律實(shí)現(xiàn)多線程的同步和互斥 - EnergySystem.java:能量類 - EnergySystemTest.java:測(cè)試Main類 - EnergyTransferTask.java:任務(wù)線程線程
synchronizedsynchronized關(guān)鍵字,代表這個(gè)方法加鎖,相當(dāng)于不管哪一個(gè)線程(例如線程A),運(yùn)行到這個(gè)方法時(shí),都要檢查有沒有其它線程B(或者C、D等)正在用這個(gè)方法(或者該類的其他同步方法),有的話要等正在使用synchronized方法的線程B(或者C、D)運(yùn)行完這個(gè)方法后再運(yùn)行此線程A,沒有的話,鎖定調(diào)用者,然后直接運(yùn)行。它包括兩種用法:synchronized 方法和 synchronized塊。 Java語(yǔ)言的關(guān)鍵字,可用來給對(duì)象和方法或者代碼塊加鎖,當(dāng)它鎖定一個(gè)方法或者一個(gè)代碼塊的時(shí)候,同一時(shí)刻最多只有一個(gè)線程執(zhí)行這段代碼。當(dāng)兩個(gè)并發(fā)線程訪問同一個(gè)對(duì)象object中的這個(gè)加鎖同步代碼塊時(shí),一個(gè)時(shí)間內(nèi)只能有一個(gè)線程得到執(zhí)行。另一個(gè)線程必須等待當(dāng)前線程執(zhí)行完這個(gè)代碼塊以后才能執(zhí)行該代碼塊。然而,當(dāng)一個(gè)線程訪問object的一個(gè)加鎖代碼塊時(shí),另一個(gè)線程仍然可以訪問該object中的非加鎖代碼塊。 不加鎖(synchronized )的情況/**
* 宇宙的能量系統(tǒng) 遵循能量守恒定律: 能量不會(huì)憑空創(chuàng)生或消失,只會(huì)從一處轉(zhuǎn)移到另一處
*/
public class EnergySystem {
// 能量盒子,能量存貯的地方
private final double[] energyBoxes;
private final Object lockObj = new Object();
/**
*
* @param n
* 能量盒子的數(shù)量
* @param initialEnergy
* 每個(gè)能量盒子初始含有的能量值
*/
public EnergySystem(int n, double initialEnergy) {
energyBoxes = new double[n];
for (int i = 0; i < energyBoxes.length; i++)
energyBoxes[i] = initialEnergy;
}
/**
* 能量的轉(zhuǎn)移,從一個(gè)盒子到另一個(gè)盒子
*
* @param from
* 能量源
* @param to
* 能量終點(diǎn)
* @param amount
* 能量值
*/
public void transfer(int from, int to, double amount) {
if (energyBoxes[from] > amount) {
System.out.print(Thread.currentThread().getName());
energyBoxes[from] -= amount;
System.out.printf("從%d轉(zhuǎn)移%10.2f單位能量到%d", from, amount, to);
energyBoxes[to] += amount;
System.out.printf(" 能量總和:%10.2f%n", getTotalEnergies());
} else {
return;
}
}
/**
* 獲取能量世界的能量總和
*/
public double getTotalEnergies() {
double sum = 0;
for (double amount : energyBoxes)
sum += amount;
return sum;
}
/**
* 返回能量盒子的長(zhǎng)度
*/
public int getBoxAmount() {
return energyBoxes.length;
}
} public class EnergyTransferTask implements Runnable{
//共享的能量世界
private EnergySystem energySystem;
//能量轉(zhuǎn)移的源能量盒子下標(biāo)
private int fromBox;
//單次能量轉(zhuǎn)移最大單元
private double maxAmount;
//最大休眠時(shí)間(毫秒)
private int DELAY = 10;
public EnergyTransferTask(EnergySystem energySystem, int from, double max){
this.energySystem = energySystem;
this.fromBox = from;
this.maxAmount = max;
}
public void run() {
int c = 3;
try{
while (c > 0){
c--;
int toBox = (int) (energySystem.getBoxAmount()* Math.random());
double amount = maxAmount * Math.random();
energySystem.transfer(fromBox, toBox, amount);
Thread.sleep((int) (DELAY * MansferTask(eng, i, INITIAL_ENERGY);
Thread t = new Thread(task,"TransferThread_"+i);
t.start();
}
}
} public class EnergySystemTest {
//將要構(gòu)建的能量世界中能量盒子數(shù)量
public static final int BOX_AMOUNT = 10;
//每個(gè)盒子初始能量
public static final double INITIAL_ENERGY = 100;
public static void main(String[] args){
EnergySystem eng = new EnergySystem(BOX_AMOUNT, INITIAL_ENERGY);
for (int i = 0; i < BOX_AMOUNT; i++){
EnergyTransferTask task = new EnergyTransferTask(eng, i, INITIAL_ENERGY);
Thread t = new Thread(task,"TransferThread_"+i);
t.start();
}
}
} 代碼功能現(xiàn)在的代碼描述的是有10個(gè)能量盒子,然后啟動(dòng)10個(gè)線程,每個(gè)線程會(huì)執(zhí)行三次操作:將A盒子中能量轉(zhuǎn)移到B盒子中,但是我們發(fā)現(xiàn),能量總和減少啦,為什么呢? 舉個(gè)例子:線程1讀取盒子A的數(shù)據(jù)5000,將其加500,此時(shí)線程2讀取盒子A的數(shù)據(jù)5000(因?yàn)檫€有寫入,所以盒子A還是5000),將其加900,再將其寫入,此時(shí)盒子A為5900,但此時(shí)線程A也要進(jìn)行寫操作,這時(shí)盒子A為5500,所以…… 輸出樣例TransferThread_3TransferThread_7TransferThread_2從2轉(zhuǎn)移 53.56單位能量到3TransferThread_6從6轉(zhuǎn)移 40.02單位能量到5 能量總和: 937.16 TransferThread_1從1轉(zhuǎn)移 2.30單位能量到8 能量總和: 937.16 TransferThread_0從0轉(zhuǎn)移 27.61單位能量到9TransferThread_9TransferThread_4從4轉(zhuǎn)移 82.48單位能量到7 能量總和: 927.92 TransferThread_8從8轉(zhuǎn)移 68.90單位能量到3 能量總和: 927.92 TransferThread_5從5轉(zhuǎn)移 33.59單位能量到3 能量總和: 927.92 TransferThread_1從9轉(zhuǎn)移 9.24單位能量到3 能量總和: 866.17 能量總和: 937.16 能量總和: 937.16 從7轉(zhuǎn)移 43.20單位能量到3 能量總和: 909.37 從3轉(zhuǎn)移 19.64單位能量到7 能量總和: 929.01 TransferThread_0從0轉(zhuǎn)移 7.07單位能量到7 能量總和: 929.01 從1轉(zhuǎn)移 70.99單位能量到2 能量總和: 1000.00 TransferThread_7從7轉(zhuǎn)移 94.67單位能量到3 能量總和: 1000.00 TransferThread_5從5轉(zhuǎn)移 66.16單位能量到3 能量總和: 1000.00 TransferThread_7從7轉(zhuǎn)移 59.33單位能量到4 能量總和: 1000.00 TransferThread_4TransferThread_6從6轉(zhuǎn)移 12.24單位能量到8 能量總和: 997.95 從4轉(zhuǎn)移 2.05單位能量到4 能量總和: 1000.00 TransferThread_3從3轉(zhuǎn)移 61.92單位能量到4 能量總和: 1000.00 TransferThread_2從2轉(zhuǎn)移 65.65單位能量到5 能量總和: 1000.00 TransferThread_8TransferThread_9從9轉(zhuǎn)移 44.34單位能量到3 能量總和: 977.95 從8轉(zhuǎn)移 22.05單位能量到4 能量總和: 1000.00 TransferThread_1從1轉(zhuǎn)移 1.77單位能量到8 能量總和: 1000.00 TransferThread_4從4轉(zhuǎn)移 25.64單位能量到5 能量總和: 1000.00 TransferThread_3從3轉(zhuǎn)移 83.27單位能量到6 能量總和: 1000.00 加鎖操作,修改EnergySystem.java/**
* 宇宙的能量系統(tǒng) 遵循能量守恒定律: 能量不會(huì)憑空創(chuàng)生或消失,只會(huì)從一處轉(zhuǎn)移到另一處
*/
public class EnergySystem {
// 能量盒子,能量存貯的地方
private final double[] energyBoxes;
private final Object lockObj = new Object();
/**
*
* @param n
* 能量盒子的數(shù)量
* @param initialEnergy
* 每個(gè)能量盒子初始含有的能量值
*/
public EnergySystem(int n, double initialEnergy) {
energyBoxes = new double[n];
for (int i = 0; i < energyBoxes.length; i++)
energyBoxes[i] = initialEnergy;
}
/**
* 能量的轉(zhuǎn)移,從一個(gè)盒子到另一個(gè)盒子
*
* @param from
* 能量源
* @param to
* 能量終點(diǎn)
* @param amount
* 能量值
*/
public void transfer(int from, int to, double amount) {
synchronized (lockObj) {
// while循環(huán),保證條件不滿足時(shí)任務(wù)都會(huì)被條件阻擋
// 而不是繼續(xù)競(jìng)爭(zhēng)CPU資源
while (energyBoxes[from] < amount) {
try {
// 條件不滿足, 將當(dāng)前線程放入Wait Set
lockObj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.print(Thread.currentThread().getName());
energyBoxes[from] -= amount;
System.out.printf("從%d轉(zhuǎn)移%10.2f單位能量到%d", from, amount, to);
energyBoxes[to] amount;
System.out.printf(" 能量總和:%10.2f%n", getTotalEnergies());
// 喚醒所有在lockObj對(duì)象上等待的線程
lockObj.notifyAll();
}
}
/**
* 獲取能量世界的能量總和
*/
public double getTotalEnergies() {
double sum = 0;
for (double amount : energyBoxes)
sum += amount;
return sum;
}
/**
* 返回能量盒子的長(zhǎng)度
*/
public int getBoxAmount() {
return energyBoxes.length;
}
} 鎖的應(yīng)用當(dāng)程序執(zhí)行到transfer方法時(shí),通過synchronized (lockObj)會(huì)將當(dāng)前資源(盒子A)鎖住,其他線程無(wú)法獲取次資源,當(dāng)energyBoxes[from]小于amount時(shí),即當(dāng)前數(shù)據(jù)不符合操作要求,執(zhí)行l(wèi)ockObj.wait()方法,將資源放開,此線程進(jìn)入Wait Set中等待其他線程喚醒(notifyAll(),notify()),當(dāng)數(shù)據(jù)符合操作且操作結(jié)束后,會(huì)調(diào)用notifyAll方法釋放線程,這是其他線程就可以獲取當(dāng)前資源了,達(dá)到總能量始終是一定的。 notify()和notifyAll()都是Object對(duì)象用于通知處在等待該對(duì)象的線程的方法。兩者的最大區(qū)別在于: notifyAll使所有原來在該對(duì)象上等待被notify的線程統(tǒng)統(tǒng)退出wait的狀態(tài),變成等待該對(duì)象上的鎖,一旦該對(duì)象被解鎖,他們就會(huì)去競(jìng)爭(zhēng)。 notify則文明得多他只是選擇一個(gè)wait狀態(tài)線程進(jìn)行通知,并使它獲得該對(duì)象上的鎖,但不驚動(dòng)其他同樣在等待被該對(duì)象notify的線程們,當(dāng)?shù)谝粋€(gè)線程運(yùn)行完畢以后釋放對(duì)象上的鎖此時(shí)如果該對(duì)象沒有再次使用notify語(yǔ)句,則即便該對(duì)象已經(jīng)空閑,其他wait狀態(tài)等待的線程由于沒有得到該對(duì)象的通知,繼續(xù)處在wait狀態(tài),直到這個(gè)對(duì)象發(fā)出一個(gè)notify或notifyAll,它們等待的是被notify或notifyAll,而不是鎖。 輸出樣例TransferThread_6從6轉(zhuǎn)移 62.69單位能量到1 能量總和: 1000.00 TransferThread_1從1轉(zhuǎn)移 88.95單位能量到2 能量總和: 1000.00 TransferThread_3從3轉(zhuǎn)移 15.16單位能量到1 能量總和: 1000.00 TransferThread_4從4轉(zhuǎn)移 37.62單位能量到2 能量總和: 1000.00 TransferThread_5從5轉(zhuǎn)移 88.83單位能量到6 能量總和: 1000.00 TransferThread_0從0轉(zhuǎn)移 57.21單位能量到7 能量總和: 1000.00 TransferThread_2從2轉(zhuǎn)移 67.80單位能量到0 能量總和: 1000.00 TransferThread_7從7轉(zhuǎn)移 55.19單位能量到8 能量總和: 1000.00 TransferThread_8從8轉(zhuǎn)移 75.56單位能量到8 能量總和: 1000.00 TransferThread_9從9轉(zhuǎn)移 21.01單位能量到6 能量總和: 1000.00 TransferThread_3從3轉(zhuǎn)移 75.10單位能量到8 能量總和: 1000.00 TransferThread_6從6轉(zhuǎn)移 17.85單位能量到4 能量總和: 1000.00 TransferThread_7從7轉(zhuǎn)移 54.41單位能量到4 能量總和: 1000.00 TransferThread_0從0轉(zhuǎn)移 65.67單位能量到6 能量總和: 1000.00 TransferThread_9從9轉(zhuǎn)移 11.83單位能量到7 能量總和: 1000.00 TransferThread_2從2轉(zhuǎn)移 59.20單位能量到8 能量總和: 1000.00 TransferThread_9從9轉(zhuǎn)移 32.79單位能量到3 能量總和: 1000.00 TransferThread_3從3轉(zhuǎn)移 11.00單位能量到9 能量總和: 1000.00 TransferThread_4從4轉(zhuǎn)移 41.82單位能量到4 能量總和: 1000.00 TransferThread_8從8轉(zhuǎn)移 53.70單位能量到3 能量總和: 1000.00 TransferThread_6從6轉(zhuǎn)移 81.85單位能量到8 能量總和: 1000.00 TransferThread_2從2轉(zhuǎn)移 15.96單位能量到0 能量總和: 1000.00 TransferThread_4從4轉(zhuǎn)移 32.41單位能量到9 能量總和: 1000.00 TransferThread_0從0轉(zhuǎn)移 59.02單位能量到0 能量總和: 1000.00 TransferThread_8從8轉(zhuǎn)移 67.92單位能量到2 能量總和: 1000.00
|