併發編程學習(14)-----顯式鎖

思維導圖:

引言:

    本文的主要內容是介紹兩種顯式鎖的使用。一種是Lock,一種是ReadWriteLock。所以,本文可以歸類爲使用部分:

  • 使用部分:介紹Lock,其實現類是ReentrantLock,功能則是補充synchronized的不足。也會介紹ReadWriteLock,用於讀多寫少的併發程序。

一.Lock

    顯式鎖Lock可以作爲synchronized在功能不足時的補充,比如定時獲取鎖這些操作。Lock並不能替代synchronized,這個小節會介紹顯式鎖Lock的一些常見用法。

1.1 加鎖方式

    如何使用Lock進行各種各樣的加鎖呢?

    1.1.1 直接加鎖

    public void addLock(int n) {
        Lock lock = new ReentrantLock();
        lock.lock();
        try{
            //別加鎖的操作
        }finally {
            //釋放鎖
            lock.unlock();
        }
    }

1.1.2 輪詢鎖

    我們可以使用Lock進行輪詢操作以獲取鎖,這樣做可以避免發生順序死鎖。如下代碼所示:

public class DeadlockAvoidance {
    private static Random rnd = new Random();

    public boolean transferMoney(Account fromAcct, Account toAcct, DollarAmount amount, long timeout, TimeUnit unit) throws InsufficientFundsException, InterruptedException {
        long fixedDelay = getFixedDelayComponentNanos(timeout, unit);
        long randMod = getRandomDelayModulusNanos(timeout, unit);
        long stopTime = System.nanoTime() + unit.toNanos(timeout);

        while (true) {
            if (fromAcct.lock.tryLock()) {
                try {
                    if (toAcct.lock.tryLock()) {
                        try {
                            if (fromAcct.getBalance().compareTo(amount) < 0) {
                                throw new InsufficientFundsException();
                            } else {
                                fromAcct.debit(amount);
                                toAcct.credit(amount);
                                return true;
                            }
                        } finally {
                            toAcct.lock.unlock();
                        }
                    }
                } finally {
                    fromAcct.lock.unlock();
                }
            }
            if (System.nanoTime() < stopTime) {
                return false;
            }
            NANOSECONDS.sleep(fixedDelay + rnd.nextLong() % randMod);
        }
    }

    private static final int DELAY_FIXED = 1;
    private static final int DELAY_RANDOM = 2;

    static long getFixedDelayComponentNanos(long timeout, TimeUnit unit) {
        return DELAY_FIXED;
    }

    static long getRandomDelayModulusNanos(long timeout, TimeUnit unit) {
        return DELAY_RANDOM;
    }

    static class DollarAmount implements Comparable<DollarAmount> {
        public int compareTo(DollarAmount other) {
            return 0;
        }

        DollarAmount(int dollars) {
        }
    }

    class Account {
        public Lock lock;

        void debit(DollarAmount d) {
        }

        void credit(DollarAmount d) {
        }

        DollarAmount getBalance() {
            return null;
        }
    }

    class InsufficientFundsException extends Exception {
    }
}

1.1.3 定時鎖

    可以利用Lock實現嘗試在規定時間內獲取鎖,超時則放棄的功能。如下所示:

public class TimedLocking {
    private Lock lock = new ReentrantLock();

    public boolean trySendOnSharedLine(String message, long timeout, TimeUnit unit) throws InterruptedException {
        long nanosToLock = unit.toNanos(timeout) - estimatedNanosToSend(message);
        if (!lock.tryLock(nanosToLock, NANOSECONDS)) {
            return false;
        }
        try {
            return sendOnSharedLine(message);
        } finally {
            lock.unlock();
        }
    }

    private boolean sendOnSharedLine(String message) {
        /* send something */
        return true;
    }

    long estimatedNanosToSend(String message) {
        return message.length();
    }
}

1.1.4 可中斷鎖

    Lock也有對中斷操作保持響應的可中斷鎖的功能,如下所示:

public class InterruptibleLocking {
    private Lock lock = new ReentrantLock();

    public boolean sendOnSharedLine(String message) throws InterruptedException {
        lock.lockInterruptibly();
        try {
            return cancellableSendOnSharedLine(message);
        } finally {
            lock.unlock();
        }
    }

    private boolean cancellableSendOnSharedLine(String message) throws InterruptedException {
        /* send something */
        return true;
    }

}

1.2 使用要點

    使用顯示鎖時有以下幾個需要注意的地方。

  1. 在併發程序發生激烈的競爭時,使用顯示鎖的速度並不會比使用synchronized更快
  2. ReetrantLock可以使用公平鎖或者非公平鎖。公平鎖時依照請求順序獲得鎖,而非公平鎖的可以插隊,如果該鎖的狀態在發出請求後恰好可用的話
  3. 使用ReetrantLock在內存的語義上和使用synchronized相同,他們提供的性能也相差不遠,所以,一般情況下還是使用synchronized比較方面,出錯的可能性也較小。但是,如果需要一些特殊的功能,比如公平性,可中斷,可等待時,就需要使用ReetrantLock了,總的來說,ReetrantLock是synchronized在功能上的補充。

二.ReadWriteLock

    對於某些併發程序來說,其讀操作的數量遠遠大於寫操作的數量。在讀取操作時,我們其實只需要保證內存的可見性就好,並不需要獲取整個鎖。只有當需要進行寫操作是,才需要獲取獨佔鎖。此時,我們就可以使用ReadWriteLock了。如下所示:

public class ReadWriteMap <K,V> {
    private final Map<K, V> map;
    private final ReadWriteLock lock = new ReentrantReadWriteLock();
    private final Lock r = lock.readLock();
    private final Lock w = lock.writeLock();

    public ReadWriteMap(Map<K, V> map) {
        this.map = map;
    }

    public V put(K key, V value) {
        w.lock();
        try {
            return map.put(key, value);
        } finally {
            w.unlock();
        }
    }

    public V get(Object key) {
        r.lock();
        try {
            return map.get(key);
        } finally {
            r.unlock();
        }
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章