熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> Java高級技術 >> 正文

Java多線程同步-BusyFlag或Lock

2013-11-23 19:51:33  來源: Java高級技術 

  我們首先開發一個BusyFlag的類類似於C++中的Simaphore



    public class BusyFlag {
        protected Thread busyflag = null;
        protected int busycount = ;
        
        public synchronized void getBusyFlag() {
            while (tryGetBusyFlag() == false) {
                try {
                    wait();
                } catch (Exception e) {}            
            }
        }
        
        private synchronized boolean tryGetBusyFlag() {
            if (busyflag == null) {
                busyflag = ThreadcurrentThread();
                busycount = ;
                return true;
            }
            
            if (busyflag == ThreadcurrentThread()) {
                busycount++;
                return true;
            }
            return false;        
        }
        
        public synchronized void freeBusyFlag() {
            if(getOwner()== ThreadcurrentThread()) {
                busycount;
                if(busycount==) {
                    busyflag = null;
                                         notify();
                                }
            }
        }
        
        public synchronized Thread getOwner() {
            return busyflag;
        }
    }

  注參考Scott Oaks & Henry Wong《Java Thread》

BusyFlag有個公開方法getBusyFlag freeBusyFlag getOwner分別用於獲取忙標志釋放忙標志和獲取當前占用忙標志的線程使用這個BusyFlag也非常地簡單只需要在需要鎖定的地方調用BusyFlag的getBusyFlag()在對鎖定的資源使用完畢時再調用改BusyFlag的freeBusyFlag()即可下面我們開始改造上篇中的Account和ATM類並應用BusyFlag工具類使得同時只有一個線程能夠訪問同一個賬戶的目標得以實現首先要改造Account類在Account中內置了一個BusyFlag對象並通過此標志對象對Account進行鎖定和解鎖



    import javautilCollections;
    import javautilHashMap;
    import javautilMap;

    class Account {
        String name;
        //float amount;
        
        BusyFlag flag = new BusyFlag();
        
        //使用一個Map模擬持久存儲
        static Map storage = new HashMap();
        static {
            storageput(John new Float(f));
            storageput(Mike new Float(f));
        }
        
        static Map accounts = CollectionssynchronizedMap(new HashMap());    
        
        
        private Account(String name) {
            thisname = name;
            //thisamount = ((Float)storageget(name))floatValue();
        }
        
        public synchronized static Account getAccount (String name) {
            if (accountsget(name) == null)
                accountsput(name new Account(name));
            return (Account) accountsget(name);
        }

        public synchronized void deposit(float amt) {
            float amount = ((Float)storageget(name))floatValue();
            storageput(name new Float(amount + amt));
        }

        public synchronized void withdraw(float amt) throws InsufficientBalanceException {
            float amount = ((Float)storageget(name))floatValue();
            if (amount >= amt)
                amount = amt;
            else 
                throw new InsufficientBalanceException();
                    
            storageput(name new Float(amount));
        }

        public float getBalance() {
            float amount = ((Float)storageget(name))floatValue();
            return amount;
        }
        
        public void lock() {
            flaggetBusyFlag();
        }
        
        public void unlock() {
            flagfreeBusyFlag();
        }
    }

  新的Account提供了兩個用於鎖定的方法lock()和unlock()供Account對象的客戶端在需要時鎖定Account和解鎖AccountAccount通過委托給BusyFlag來提供這個機制另外大家也發現了新的Account中提供了對Account對象的緩存同時去除了public的構造方法改為使用一個靜態工廠方法供用戶獲取Account的實例這樣做也是有必要的因為我們希望所有的ATM機同時只能有一個能夠對同一個Account進行操作我們在Account上的鎖定是對一個特定Account對象進行加鎖如果多個ATM同時實例化多個同一個user的Account對象那麼仍然可以同時操作同一個賬戶所以要使用這種機制就必須保證Account對象在系統中的唯一性所以這兒使用一個Account的緩存並將Account的構造方法變為私有的你也可以說通過在Account類鎖上進行同步即將Account中的BusyFlag對象聲明為static的但這樣就使同時只能有一台ATM機進行操作了這樣在一台ATM機在操作時全市其它的所有的ATM機都必須等待
另外必須注意的一點是Account中的getAccount()方法必須同步否則將有可能生成多個Account對象因為可能多個線程同時到達這個方法並監測到accounts中沒有John的Account實例從而實例化多個John的Account實例s

ATM類只需作少量改動在login方法中鎖定Account在logout方法中解鎖



    public class ATM {
        Account acc;
        
        //作為演示省略了密碼驗證
        public synchronized boolean login(String name) {
            if (acc != null)
                throw new IllegalArgumentException(Already logged in!);
            acc = AccountgetAccount(name);
            acclock();
            return true;
        }
        
        public void deposit(float amt) {
            accdeposit(amt);
        }
        
        public void withdraw(float amt) throws InsufficientBalanceException  {
                accwithdraw(amt);
        }
        
        public float getBalance() {
            return accgetBalance();
        }
        
        public synchronized void logout () {
            accunlock();
            acc = null;
        }
        
    }

  ATMTester類不需要做任何修改即可同樣運行同時保證同一個Account同時只能由一個ATM進行操作解決了上篇提到的多個ATM同時對同一個Account進行操作造成的問題

在最新的Doug Lea的ncurrent工具包中(現處於JSR)提供了類似的並發實用類ReentrantLock它實現了java ncurrentlocksLock接口(將在JDK中發布)它的作用也類似於我們這兒的BusyFlag實現機制使用方法也相似但這是一個工業強度的可重入鎖的實現類在ReentrantLock的API文檔中有它的使用示例



         Lock l = 
         llock();
         try {
             // access the resource protected by this lock
         } finally {
             lunlock();
         }

From:http://tw.wingwit.com/Article/program/Java/gj/201311/27585.html
    推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.