显式锁

Lock和ReentrantLock:

与内置加锁机制(synchronized)不同的是,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相同的互斥性和内存可见性。在获取ReentrantLock时有着与进入同步代码块相同的内存语义;在释放ReentrantLock时,有着与退出同步代码块相同的内存语义。

使用ReentrantLock必须在finally块中释放锁。ReentrantLock使用方法如下:

Lock lock = new ReentrantLock();
...
lock.lock();
try{
    ...
}finally{
    lock.unlock();
}

轮询锁、定时锁和中断锁

定时锁和轮询锁的获取方式是由tryLock()方法实现的,与无条件的锁的获取方式相比,它具有更完善的错误恢复机制。

在内置锁中,死锁是一个严重的问题,恢复程序的唯一方法是重新启动程序,而防止死锁的唯一方法就是构造程序时避免出现不一致的锁顺序。可定时和可轮询的锁提供了另一种选择:避免死锁的发生。

如果不能获取所有需要的锁,那么可以使用定时锁或轮询锁,它会释放已经获取的锁,并重新尝试获取所有锁。

轮询锁:

public boolean Polling(Account fromAcct, Account toAcct){
    while(true){
        if(fromAcct.lock.tryLock()){
            try{
                if(toAcct.lock.tryLock()){
                    try{
                        //业务逻辑
                    }finally{
                         toAcct.lock.unlock();
                    }
                }
            }finally{
                 fromAcct.lock.unlock();
            }
        }
    }
    return false;
}

定时锁:

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 sendOnShareLine(message);
    }finally{
        lock.unlock();
    }
}

中断锁:

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

公平性

ReentrantLock的构造函数提供了两种公平性选择:创建一个非公平锁(默认)和创建一个公平锁。公平锁按照线程发出请求的顺序获得锁;非公平锁允许“插队”:如果一个线程发出请求的同时正好该锁的状态变为可用,则该线程直接获得锁而不需要排队。

在大多数情况下,非公平锁的性能大于公平锁。一个原因是:在恢复一个被挂起的线程和该线程真正执行之间存在很大的延迟。

当持有锁的时间相对较长,或者请求锁的平均时间间隔较长,那么应该使用公平锁。默认的内置锁(synchronized)并不提供公平性保证。

内置锁和显式锁:

当需要一些高级功能,内置锁无法实现时,才应该使用ReentrantLock, 这些功能包括:可定时的、可轮询的与可中断的锁获取操作,公平队列,以及非块结构的锁。否则就应该使用内置锁(synchronized)。

ReentrantLock的非块结构特性意味着获取锁的操作不能和特定的栈帧关联起来,而内置锁可以。

读写锁:

ReentrantLock是一种强硬的互斥加锁规则:每次只允许一个线程获得锁。这种规则虽然避免的“读-写”冲突和“写-写”冲突,但也避免的“读-读”冲突。但实际上数据结构上的操作大多是读操作,如何可以放宽加锁规则,允许多个执行读操作的线程同时访问数据结构,则可以使用“读写锁”:一个资源可以同时被多个读操作访问,或则被一个写操作访问,但两者不可同时进行。

ReadWriteLock和ReenReadWriteLock:

ReadWriteLock接口:

public interface ReadWriteLock{
    Lock readLock();
    Lock writeLock();
}

ReenReadWriteLock是ReadWriteLock的实现类。ReenReadWriteLock为两种锁都提供了可重入加锁语义。ReenReadWriteLock在构造时也可以选择是非公平锁(默认)或者是公平锁。ReenReadWriteLock的写入锁只能有一个拥有者,并且只能由获得该锁的线程来释放。

public class ReadWriteMap<K,V>{
    private final Map<K,V> map;
    private final ReadWriteLock lock = new ReenReadWriteLock();
    private final Lock r = lock.readLock();
    private final Lock r = 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();
        }
    }  
}  

猜你喜欢

转载自my.oschina.net/HuoQibin/blog/1807026