并发编程学习(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();
        }
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值