小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

2.使用synchronized關(guān)鍵字實(shí)現(xiàn)多線程的同步和互斥(不同線程同時(shí)讀寫同一數(shù)據(jù))

 quandsu 2020-11-25

利用能量守恒定律實(shí)現(xiàn)多線程的同步和互斥
- EnergySystem.java:能量類
- EnergySystemTest.java:測(cè)試Main類
- EnergyTransferTask.java:任務(wù)線程線程


synchronized

synchronized關(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

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多