Lock与ReentrantLock

来源:互联网 发布:禁闭岛影评 知乎 编辑:程序博客网 时间:2024/06/05 10:56

    Lock提供了一种无条件的、可轮询的、定时的、可中断的锁获取操作,所有加锁和解锁的方法都是显式的。

Lock接口如下:

public interface Lock {    void lock();    void lockInterruptibly() throws InterruptedException;    boolean tryLock();        boolean tryLock(long time, TimeUnit unit) throws InterruptedException;         void unlock();         Condition newCondition();}

ReentrantLock实现了Lock,提供了与Synchronized相同的互斥性和内存可见性。ReentrantLock提供了可重入的加锁语义。与Synchronized相比,它还为处理锁的不可用性问题提供了更高的灵活性。

    内置锁的局限性:

    1.无法中断一个正在等待获取锁的线程

    2.无法再请求获取一个锁时无限地等待下去

    3.必须在获取该锁的代码块中释放

    4.无法实现非阻塞结构的加锁规则

    5.死锁严重(恢复程序的唯一方法是重新启动程序,防止死锁的唯一方法是在构造程序时避免出现不一致的锁顺序)

使用ReentrantLock来保护对象状态

Lock lock = new ReentrantLock();...lock.lock();try{//更新对象状态//捕获异常,并在必要时恢复不变性条件}finally{lock.unlock(); //必须关闭,不然很危险}

必须在finally块中释放锁,否则,如果在被保护的代码中抛出了异常,那么这个锁永远都无法释放。当使用加锁时,还必须考虑在try块中抛出异常的情况,如果可能使对象处于某种不一致的状态,那么就需要更多的try-catch或try-finally代码块。

    当忘记关闭时,将很难追踪到最初发生错误的位置,因为没有记录应该释放锁的位置和时间。

    ReentrantLock不能完全替代Synchronized的原因:它更危险,可能忘记关闭锁。

ReentrantLock源代码基本如下:

public class ReentrantLock implements Lock, java.io.Serializable {    private final Sync sync;    abstract static class Sync extends AbstractQueuedSynchronizer {        abstract void lock();        final boolean nonfairTryAcquire(int acquires) {            final Thread current = Thread.currentThread();            int c = getState();            if (c == 0) {                if (compareAndSetState(0, acquires)) {                    setExclusiveOwnerThread(current);                    return true;                }            }            else if (current == getExclusiveOwnerThread()) {                int nextc = c + acquires;                if (nextc < 0) // overflow                    throw new Error("Maximum lock count exceeded");                setState(nextc);                return true;            }            return false;        }        protected final boolean tryRelease(int releases) {            int c = getState() - releases;            if (Thread.currentThread() != getExclusiveOwnerThread())                throw new IllegalMonitorStateException();            boolean free = false;            if (c == 0) {                free = true;                setExclusiveOwnerThread(null);            }            setState(c);            return free;        }        protected final boolean isHeldExclusively() {            // While we must in general read state before owner,            // we don't need to do so to check if current thread is owner            return getExclusiveOwnerThread() == Thread.currentThread();        }        final ConditionObject newCondition() {            return new ConditionObject();        }        final Thread getOwner() {            return getState() == 0 ? null : getExclusiveOwnerThread();        }        final int getHoldCount() {            return isHeldExclusively() ? getState() : 0;        }        final boolean isLocked() {            return getState() != 0;        }        private void readObject(java.io.ObjectInputStream s)            throws java.io.IOException, ClassNotFoundException {            s.defaultReadObject();            setState(0); // reset to unlocked state        }    }    static final class NonfairSync extends Sync {        final void lock() {            if (compareAndSetState(0, 1))                setExclusiveOwnerThread(Thread.currentThread());            else                acquire(1);        }        protected final boolean tryAcquire(int acquires) {            return nonfairTryAcquire(acquires);        }    }    static final class FairSync extends Sync {          final void lock() {            acquire(1);        }        protected final boolean tryAcquire(int acquires) {            final Thread current = Thread.currentThread();            int c = getState();            if (c == 0) {                if (!hasQueuedPredecessors() &&                    compareAndSetState(0, acquires)) {                    setExclusiveOwnerThread(current);                    return true;                }            }            else if (current == getExclusiveOwnerThread()) {                int nextc = c + acquires;                if (nextc < 0)                    throw new Error("Maximum lock count exceeded");                setState(nextc);                return true;            }            return false;        }    }    public ReentrantLock() {        sync = new NonfairSync();    }    public ReentrantLock(boolean fair) {        sync = fair ? new FairSync() : new NonfairSync();    }    public void lock() {        sync.lock();    }    public void lockInterruptibly() throws InterruptedException {        sync.acquireInterruptibly(1);    }    public boolean tryLock() {        return sync.nonfairTryAcquire(1);    }    public boolean tryLock(long timeout, TimeUnit unit)            throws InterruptedException {        return sync.tryAcquireNanos(1, unit.toNanos(timeout));    }    public void unlock() {        sync.release(1);    }    public Condition newCondition() {        return sync.newCondition();    }    public int getHoldCount() {        return sync.getHoldCount();    }    public boolean isHeldByCurrentThread() {        return sync.isHeldExclusively();    }    public boolean isLocked() {        return sync.isLocked();    }    public final boolean isFair() {        return sync instanceof FairSync;    }    protected Thread getOwner() {        return sync.getOwner();    }    public final boolean hasQueuedThreads() {        return sync.hasQueuedThreads();    }    public final boolean hasQueuedThread(Thread thread) {        return sync.isQueued(thread);    }    public final int getQueueLength() {        return sync.getQueueLength();    }    protected Collection<Thread> getQueuedThreads() {        return sync.getQueuedThreads();    }    public boolean hasWaiters(Condition condition) {        if (condition == null)            throw new NullPointerException();        if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))            throw new IllegalArgumentException("not owner");        return sync.hasWaiters((AbstractQueuedSynchronizer.ConditionObject)condition);    }    public int getWaitQueueLength(Condition condition) {        if (condition == null)            throw new NullPointerException();        if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))            throw new IllegalArgumentException("not owner");        return sync.getWaitQueueLength((AbstractQueuedSynchronizer.ConditionObject)condition);    }    protected Collection<Thread> getWaitingThreads(Condition condition) {        if (condition == null)            throw new NullPointerException();        if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))            throw new IllegalArgumentException("not owner");        return sync.getWaitingThreads((AbstractQueuedSynchronizer.ConditionObject)condition);    }    public String toString() {        Thread o = sync.getOwner();        return super.toString() + ((o == null) ?                                   "[Unlocked]" :                                   "[Locked by thread " + o.getName() + "]");    }}

1.轮询锁与定时锁

轮询锁与定时锁是由tryLock方法实现的,与无条件的锁获取操作相比,它们具有更完善的错误恢复机制。它们可以避免死锁的发生。


通过tryLock来避免锁顺序死锁:使用tryLock来获取两个锁,如果不能同时获得,那么就回退并重新尝试。在休眠时间中包括固定部分和随机部分,从而降低发生活锁的可能性。如果在指定时间内不能获得所有需要的锁,那么transferMoney将返回一个失败状态,从而使该操作平缓地失败。


public boolean transferMoney(Account fromAcct,Account toAcct,DollarAmount amount,long timeout,TimeUnit unit) throws InsufficientFundsException,InterruptedException{long fixedDelay = getFixedDelayComponentNanos(timeout,unit);long randMod = getRandomDelayModulesNanos(timeout,unit);long stopTime = System.nanoTime() + unit.toNanos(timeout);while(true){if(fromAcct.lock.tryLock()){try{if(toAcct.lock.tryLock()){try{if(fromAcct.getBalances.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);       }}
带有时间限制的加锁

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();}}

2.可中断的锁获取操作

可中断的锁获取操作同样能在可取消的操作中使用加锁。

lockInterruptibly方法能够在获得锁的同时保持对中断的响应,并且由于它包含在Lock中,因此无需创建其它类型的不可中断阻塞机制。

    可中断的锁获取操作的标准结构比普通的锁获取操作略微复杂一些,因为需要两个try块。(如果在可中断的锁获取操作中抛出了InterruptedException,那么可以使用标准的try-finally加锁模式。)

    定时的tryLock同样能响应中断,因此当需要实现一个定时的和可中断的锁获取操作时,可以使用tryLock方法。

可中断的锁获取操作

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

3.非块结构的加锁

    通过降低锁的粒度可以提高代码的可伸缩性。

    锁分段技术在基于散列的容器中实现了不同的散列链,以便使用不同的锁。我们可以通过采用类似的原则来降低链表中锁的粒度,即为每个链表节点使用一个独立的锁,使不同的线程能独立地对链表的不同部分进行操作。每个节点的锁将保护连接指针以及在该节点中存储的数据,因此当遍历或修改链表时,我们必须持有该节点上的这个锁,直到获得了下一个节点的锁,只有这样,才能释放前一个节点上的锁。

    连锁式加锁(Hand-Over-Hand Locking)或锁耦合(Lock Coupling)



0 0
原创粉丝点击