Java中多線程如何使用互斥鎖實現資源共享


  假設這樣一個情景:在銀行的營業廳內先后進來3個人,他們都要進行存款,若是只有一個營業窗口的話,通常的情況是每人都需要先領取順序條,然后按序排隊辦理業務,而營業廳會根據號碼的順序依次叫號來處理顧客的問題。

  在這里銀行的窗口就可以看做共享的資源,它每次只能接待一個顧客,而不同的顧客則可以看做是多個線程,他們都需要辦理業務,但是又必須遵守先來后到的原則,排隊等待前面的顧客辦理完業務才能輪到自己獨占窗口辦理自己的業務(當然也可能存在插隊現象,后面會講到)。

  為了簡化銀行處理業務的過程,我們假設每個客戶只是辦理簡單的存款業務,銀行窗口只需清點出存款數額就代表業務處理完成。

(1)首先我們定義一個銀行類Bank,用它來模擬銀行。為了能夠讓整個過程可視化,我們讓這個類繼承自Applet。而這個銀行的主業務--存款(其實就是點錢),我們用方法countMoney來模擬,由於這是個臨界資源,所以我們要給他加上互斥鎖synchronize,以保證每次只有一個顧客能夠使用這個功能。

public class Bank extends Applet{
  ......
  
public synchronized void countMoney(String name, int total){ int count = 1; while(count <= total){ t.setText("(" + name + ") " + Integer.toString(count++)); } notifyAll(); }
  ...... }

  為了能夠看清楚這個排隊等待數錢的過程,我們需要兩個文本區域,一個用來顯示當前正在辦理業務的客戶數錢的過程,另一個顯示等待數錢的客戶隊列。

public TextField counting= new TextField(10);
public TextField deque = new TextField(100);

  另外我們給該銀行模擬了三個用戶,他們分別是馬雲、雷軍和馬化騰--。

private MaYun maYun = null;
private LeiJun leiJun = null;
private MaHuaTeng maHuaTeng = null;

  整個Bank類如下所示,其中方法setDeque表示向排隊隊列中加入顧客。

public class Bank extends Applet{
    
    public TextField counting = new TextField(10);
    public TextField deque = new TextField(100);
    private MaYun maYun = null;
    private LeiJun leiJun = null;
    private MaHuaTeng maHuaTeng = null;
    
    public void init() {
        add(counting);
        add(deque);
        maYun = new MaYun(Bank.this);
        leiJun = new LeiJun(Bank.this);
        maHuaTeng = new MaHuaTeng(Bank.this);
    }
    
    public void setDequeue(String person){
        String list = deque.getText();
        list = list + person + "->";
        deque.setText(list);
    }
    
    
    public synchronized void countMoney(String name, int total){
        int count = 1;
        while(count <= total){
            counting.setText("(" + name + ") " + Integer.toString(count++));
        }
        notifyAll();
    }
    
    public static void main(int argc, String args[]){
        Bank applet = new Bank();
        Frame aFrame = new Frame("Counter2");
        aFrame.add(applet, BorderLayout.CENTER);
        aFrame.setSize(300, 200);
        applet.init();
        applet.start();
        aFrame.setVisible(true);
    }
}
View Code

(2)定義三個線程類,在每個線程類的run方法中都調用銀行類中的countMoney方法。由於countMoney加了互斥鎖,所以每次只有一個線程可以執行這個方法,而其他的線程都處於阻塞狀態。一旦一個縣城執行完了countMoney方法,他就會調用notifyAll方法,喚醒其他阻塞的線程。這個過程就模仿了顧客在營業廳排隊辦理業務的場景。

public class MaYun extends Thread {
    
    private static int money = 10000;
    private static String name = "馬雲";
    
    Bank c = null;
    Button start = new Button(name);

    public MaYun(Bank c){
        this.c = c;
        start.addActionListener(new StartListener());
        c.add(start);
    }

    public void run() {
        c.setDequeue(name);
        c.countMoney(name, money);
    }
    
    public class StartListener implements ActionListener{
        public void actionPerformed(ActionEvent e) {
            MaYun.this.start();
        }    
    };
}
View Code
public class LeiJun extends Thread {
    
    private static int money = 8000;
    private static String name = "雷軍";
    
    Bank c = null;
    Button start = new Button(name);

    public LeiJun(Bank c){
        this.c = c;
        start.addActionListener(new StartListener());
        c.add(start);
    }
        
    public void run() {
        c.setDequeue(name);
        c.countMoney(name, money);
    }
    
    public class StartListener implements ActionListener{
        public void actionPerformed(ActionEvent e) {
            LeiJun.this.start();
        }    
    };    
}
View Code
public class MaHuaTeng extends Thread {
    
    private static int money = 20000;
    private static String name = "馬化騰";
    
    Bank c = null;
    Button start = new Button(name);

    public MaHuaTeng(Bank c){
        this.c = c;
        start.addActionListener(new StartListener());
        c.add(start);
    }
        
    public void run() {
        c.setDequeue(name);
        c.countMoney(name, money);
    }
    
    public class StartListener implements ActionListener{
        public void actionPerformed(ActionEvent e) {
            MaHuaTeng.this.start();
        }    
    };    
}
View Code

(3)下面給出實驗過程中運行的界面結果。我們依次點擊馬雲、雷軍、馬化騰三個按鈕,表示這3個人依次來銀行辦理業務。由於馬雲是最先來到的,所以他可以立即到窗口辦理點錢業務。而由於處理業務的窗口只有一個,雷軍和馬化騰只能等馬雲結束,他們才能辦理自己的業務。但是在實際的運行過程中,我們發現馬雲辦理完業務后,緊接着辦理業務的不是雷軍而是馬化騰。這主要是因為當馬雲辦理完業務后,雷軍和馬化騰並沒有按照先來后到的排隊順序來,馬化騰插隊了。這說明使用notifyAll方法,所有阻塞的線程在獲得臨近資源的機會上是均等的,誰先搶到,誰先用。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM