显式锁

Lock与ReentrantLock

与内置加锁机制不同的是,Lock提供了一种无条件的、可轮询的、定时的以及可中断的锁获取操作,所有加锁的方法都是显式的。Lock接口代码如下:

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

ReentrantLock实现Lock接口,并提供令人与synchronized相同的互斥性和内存可见性。与synchronized相比,它还为处理锁的不可用性问题提供了更高的灵活性。

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

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

轮询锁与定时锁

可定时的与可轮询的锁获取模式是由tryLock方法实现的,与无条件的锁获取模式相比,它具有更完善的错误恢复机制。在内置锁中,死锁是一个严重的问题,恢复程序的唯一方法是重新启动程序,而防止死锁的唯一方法就是在构造程序时避免出现不一致的锁顺序。可定时与可轮询的锁提供了另一种选择:避免死锁的发生。

以下代码通过tryLock来获取两个锁,如果不能同时获得,那么就回退并重新尝试。

while(true){
  if(fromAcct.lock.tryLock()){
    try{
        if(toAcct.lock.tryLock(){
            try{
               ... 
            }finally{
               toAcct.lock.unLock();
            }
        }
    }finally{
        fromAcct.lock.unlock();
    }
  }
}

Condition 对象

Condition是一种广义的内置条件队列,代码如下:

public interface Condition{
    void await() throws InterruptedExcaption;
    boolean await(long time, TimeUnit unit) throws InterruptedException;
    long awaitNanos(long nanosTimeout) throws InterruptedException;
    void awaitUninterruptibly();
    boolean awaitUntil(Date deadline) throws InterruptedException;

    void signal();
    void signalAll();
}

每个内置锁都只能有一个相关联的条件队列,因而在想BoundedBuffer这种类中,多个线程可能在同一个条件队列上等待不同的条件谓词,并且在最常见的加锁模式下公开条件队列对象。这些因素都使得无法满足notifyAll时所有等待线程为同一类型的需求。如果想编写一个带有多个条件谓词的并发对象,或者想获得出条件队列可见性之外的更多控制权,就可以使用显式的Lock和Condition而不是内置锁和条件队列,这是一种更灵活的选择。

以下代码给出了有界缓存的另一种实现,即使用两个Condition,分别为notFull和notEmpty,用于表示“非满”与“非空”两个条件谓词,代码如下:

package beidao.multithread.demo;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ConditionBoundedBuffer<T> {

    private static final int BUFFER_SIZE = 100;

    protected final Lock lock = new ReentrantLock();

    //条件谓词:notFull(count < items.length)
    private final Condition notFull = lock.newCondition();

    //条件谓词:notEmpty(count > 0)
    private final Condition notEmpty = lock.newCondition();

    @SuppressWarnings("unchecked")
    private  final T[] items = (T[])new Object[BUFFER_SIZE];

    private int tail, head, count;

    //阻塞,直到:notFull
    public void put(T x) throws InterruptedException{
        lock.lock();
        try{
            while(count == items.length){
                notFull.await();
            }
            items[tail] = x;
            if(++tail == items.length){
                tail = 0;
            }
            ++count;
            notEmpty.signal();
        }finally{
            lock.unlock();
        }
    }

    //阻塞,直到:notEmpty
    public T take() throws InterruptedException{
        lock.lock();
        try{
            while(count == 0)
                notEmpty.await();
            T x = items[head];
            items[head] = null;
            if(++head == items.length)
                head = 0;
            --count;
            notFull.signal();
            return x;
        }finally {
            lock.unlock();
        }
    }

}

当使用显式的Lock和Condition时,也必须满足锁、条件谓词和条件变量之间的三元关系。在条件谓词中包含的变量必须由Lock来保护,并且在检查条件谓词以及调用await和signal时,必须持有Lock对象。

ReentrantLock的常用方法

  • int getHoldCount():查询当前线程保持此锁定的个数,也就是调用lock()方法的次数。
  • int getQueueLength():返回正等待获取此锁定的线程估计数。
  • int getWaitQueueLength(Condition):返回等待与此锁定相关的给定条件Condition的线程估计数。
  • boolean hasQueuedThread(Thread):查询指定的线程是否正在等待获取此锁定。
  • boolean hasQueuedThreads():查询是否有线程正在等待获取此锁定。
  • boolean hasWaiters(Condition):查询是否有线程正在等待与此锁定有关的condition条件。
  • boolean isFair():判断是不是公平锁,默认情况下ReentrantLock是非公平锁。
  • boolean isHeldByCurrentThread():查询当前线程是否保持此锁定。
  • boolean isLocked():查询此锁定是否由任意线程保持。
  • void lockInterruptibly():如果当前线程未被中断,则获取锁定,如果已经被中断则出现异常。
  • boolean tryLock():仅在调用时锁定未被另一个线程保持的情况下才获得该锁定。
  • boolean tryLock(long timeout,TimeUnit unit):如果锁定在给定等待时间内没有被另一个线程保持,且当前线程未被中断,则获取该锁定。
    # ReentrantReadWriteLock
    类ReentrantLock具有完全互斥排他的效果,即同一时间只有一个线程在执行ReentrantLock.lock()方法后面的任务。而JDK提供了一种读写锁ReentrantReadWriteLock类,使用它可以加快运行效率,在某些不需要操作实例变量的方法中,完全可以使用读写锁ReentrantReadWriteLock来提升该方法的代码运行速度。

读写锁表示也有两个锁,一个是读操作相关的锁,也成共享锁;另一个是写操作相关的锁,也叫排他锁。也就是多个读锁之间不互斥,读锁与写锁互斥,写锁与写锁互斥

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值